# ios 繪畫之涂鴉,貼圖,馬賽克,高斯筆涂鴉

ios 繪畫之涂鴉,貼圖,馬賽克,高斯筆涂鴉

https://github.com/wangjinshan/IJSPhotoSDK
項(xiàng)目展示

001.PNG
002.PNG
003.PNG
004.PNG
005.PNG
006.PNG
007.PNG
009.PNG
010.PNG

前言

Quartz 2D是一個(gè)二維圖形繪制引擎,支持iOS環(huán)境和Mac OS X環(huán)境压恒。我們可以使用Quartz 2D API來(lái)實(shí)現(xiàn)許多功能暇检,如基本路徑的繪制拇泣、透明度阳似、描影、繪制陰影昌抠、透明層锄贷、顏色管理、反鋸齒牢酵、PDF文檔生成和PDF元數(shù)據(jù)訪問(wèn).
當(dāng)你的程序進(jìn)行位圖繪制時(shí)悬包,不管使用哪種方式,都是基于 Quartz 2D 的,也就是說(shuō)馍乙,CPU 部分實(shí)現(xiàn)的繪制是通過(guò) Quartz 2D 實(shí)現(xiàn)的布近。盡管 Quartz 可以做其它的事情,但是我們這里還是集中于位圖繪制丝格,在緩沖區(qū)(一塊內(nèi)存)繪制位圖會(huì)包括 RGBA 數(shù)據(jù)
簡(jiǎn)單例子:
UIKit實(shí)現(xiàn)

UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(16.72, 7.22)];
[path addLineToPoint:CGPointMake(3.29, 20.83)];
[path addLineToPoint:CGPointMake(0.4, 18.05)];
[path addLineToPoint:CGPointMake(18.8, -0.47)];
[path addLineToPoint:CGPointMake(37.21, 18.05)];
[path addLineToPoint:CGPointMake(34.31, 20.83)];
[path addLineToPoint:CGPointMake(20.88, 7.22)];
[path addLineToPoint:CGPointMake(20.88, 42.18)];
[path addLineToPoint:CGPointMake(16.72, 42.18)];
[path addLineToPoint:CGPointMake(16.72, 7.22)];
[path closePath];
path.lineWidth = 1;
[[UIColor redColor] setStroke];
[path stroke];

相對(duì)應(yīng)的 Core Graphics 代碼:

CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, 16.72, 7.22);
CGContextAddLineToPoint(ctx, 3.29, 20.83);
CGContextAddLineToPoint(ctx, 0.4, 18.05);
CGContextAddLineToPoint(ctx, 18.8, -0.47);
CGContextAddLineToPoint(ctx, 37.21, 18.05);
CGContextAddLineToPoint(ctx, 34.31, 20.83);
CGContextAddLineToPoint(ctx, 20.88, 7.22);
CGContextAddLineToPoint(ctx, 20.88, 42.18);
CGContextAddLineToPoint(ctx, 16.72, 42.18);
CGContextAddLineToPoint(ctx, 16.72, 7.22);
CGContextClosePath(ctx);
CGContextSetLineWidth(ctx, 1);
CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextStrokePath(ctx);

ctx是什么?
正好引出所謂的 CGContext 登場(chǎng)撑瞧。我們傳過(guò)去的ctx參數(shù)正是在那個(gè)上下文中。而這個(gè)上下文定義了我們需要繪制的地方显蝌。如果我們實(shí)現(xiàn)了 CALayer 的 -drawInContext: 這時(shí)已經(jīng)傳過(guò)來(lái)一個(gè)上下文预伺。繪制到這個(gè)上下文中的內(nèi)容將會(huì)被繪制到圖層的備份區(qū)(圖層的緩沖區(qū)).但是我們也可以創(chuàng)建我們自己的上下文,叫做基于位圖的上下文曼尊,比如 CGBitmapContextCreate().這個(gè)方法返回一個(gè)我們可以傳給 CGContext 方法來(lái)繪制的上下文酬诀。

注意 UIKit 版本的代碼為何不傳入一個(gè)上下文參數(shù)到方法中?這是因?yàn)楫?dāng)使用 UIKit 或者 AppKit 時(shí)骆撇,上下文是唯一的瞒御。UIkit 維護(hù)著一個(gè)上下文堆棧,UIKit 方法總是繪制到最頂層的上下文中神郊。你可以使用 UIGraphicsGetCurrentContext() 來(lái)得到最頂層的上下文肴裙。你可以使用 UIGraphicsPushContext() 和 UIGraphicsPopContext() 在 UIKit 的堆棧中推進(jìn)或取出上下文。

最為突出的是涌乳,UIKit 使用 UIGraphicsBeginImageContextWithOptions() 和 UIGraphicsEndImageContext() 方便的創(chuàng)建類似于 CGBitmapContextCreate() 的位圖上下文践宴。混合調(diào)用 UIKit 和 Core Graphics 非常簡(jiǎn)單

UIGraphicsBeginImageContextWithOptions(CGSizeMake(45, 45), YES, 2);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, 16.72, 7.22);
CGContextAddLineToPoint(ctx, 3.29, 20.83);
...
CGContextStrokePath(ctx);
UIGraphicsEndImageContext();

解釋一下UIGraphicsBeginImageContextWithOptions函數(shù)參數(shù)的含義:
第一個(gè)參數(shù)表示所要?jiǎng)?chuàng)建的圖片的尺寸爷怀;
第二個(gè)參數(shù)用來(lái)指定所生成圖片的背景是否為不透明阻肩,如上我們使用YES而不是NO,則我們得到的圖片背景將會(huì)是黑色,顯然這不是我想要的烤惊;
第三個(gè)參數(shù)指定生成圖片的縮放因子乔煞,這個(gè)縮放因子與UIImage的scale屬性所指的含義是一致的。傳入0則表示讓圖片的縮放因子根據(jù)屏幕的分辨率而變化柒室,所以我們得到的圖片不管是在單分辨率還是視網(wǎng)膜屏上看起來(lái)都會(huì)很好
也可以寫成:

CGContextRef ctx = CGBitmapContextCreate(NULL, 90, 90, 8, 90 * 4, space, bitmapInfo);
CGContextScaleCTM(ctx, 0.5, 0.5);
UIGraphicsPushContext(ctx);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(16.72, 7.22)];
[path addLineToPoint:CGPointMake(3.29, 20.83)];
...
[path stroke];
UIGraphicsPopContext(ctx);
CGContextRelease(ctx);

注意: 必須要有UIGraphicsEndImageContext(); CGContextRelease(ctx); 否則會(huì)造成內(nèi)存泄露,程序卡死

其他相關(guān)的操作

繪制

#pragma mark 繪制矩形
-(void)_drawRectWithContext:(CGContextRef)context
{
    //添加矩形對(duì)象
    CGRect rect=CGRectMake(20, 50, 280, 50);
    CGContextAddRect(context,rect);
    //設(shè)置屬性
    [[UIColor blueColor]set];
    //繪制
    CGContextDrawPath(context, kCGPathFillStroke);
}
#pragma mark 繪制矩形(利用UIKit的封裝方法)
-(void)_drawRectByUIKitWithContext:(CGContextRef)context
{
    CGRect rect= CGRectMake(20, 150, 280.0, 50.0);
    CGRect rect2=CGRectMake(20, 250, 280.0, 50.0);
    //設(shè)置屬性
    [[UIColor yellowColor]set];
    //繪制矩形,相當(dāng)于創(chuàng)建對(duì)象渡贾、添加對(duì)象到上下文、繪制三個(gè)步驟
    UIRectFill(rect);//繪制矩形(只有填充)
    [[UIColor redColor]setStroke];
    UIRectFrame(rect2);//繪制矩形(只有邊框)
}
#pragma mark 繪制橢圓
-(void)_drawEllipse:(CGContextRef)context
{
    //添加對(duì)象,繪制橢圓(圓形)的過(guò)程也是先創(chuàng)建一個(gè)矩形
    CGRect rect=CGRectMake(50, 50, 220.0, 180.0);
    CGContextAddEllipseInRect(context, rect);
    //設(shè)置屬性
    [[UIColor purpleColor]set];
    //繪制
    CGContextDrawPath(context, kCGPathFillStroke);
}
#pragma mark - 繪制圓弧
-(void)_drawArc:(CGContextRef)context
{
    /*添加弧形對(duì)象
     x:中心點(diǎn)x坐標(biāo)
     y:中心點(diǎn)y坐標(biāo)
     radius:半徑
     startAngle:起始弧度
     endAngle:終止弧度
     closewise:是否逆時(shí)針繪制雄右,0則順時(shí)針繪制
     */
    CGContextAddArc(context, 160, 160, 100.0, 0.0, M_PI_2, 1);
    //設(shè)置屬性
    [[UIColor yellowColor]set];
    //繪制
    CGContextDrawPath(context, kCGPathStroke);   // kCGPathStroke 描邊  kCGPathFillStroke填充
}
#pragma mark - 繪制文字
-(void)_drawText:(CGContextRef)context
{
    //繪制到指定的區(qū)域內(nèi)容
    NSString *str=@"這是一個(gè)非常牛逼的操作啊";
    CGRect rect= CGRectMake(20, 50, 280, 300);
    UIFont *font=[UIFont systemFontOfSize:18];//設(shè)置字體
    UIColor *color=[UIColor redColor];//字體顏色
    NSMutableParagraphStyle *style=[[NSMutableParagraphStyle alloc]init];//段落樣式
    NSTextAlignment align=NSTextAlignmentLeft;//對(duì)齊方式
    style.alignment=align;
    NSDictionary<NSAttributedStringKey, id> *attributeDic = @{NSFontAttributeName:font,NSForegroundColorAttributeName:color,NSParagraphStyleAttributeName:style};
    [str drawInRect:rect withAttributes:attributeDic];
}
#pragma mark - 繪制圖片
-(void)_drawImage:(CGContextRef)context
{
    UIImage *image=[UIImage imageNamed:@"8.png"];
    //從某一點(diǎn)開始繪制
    [image drawAtPoint:CGPointMake(10, 50)];
    //繪制到指定的矩形中空骚,注意如果大小不合適會(huì)會(huì)進(jìn)行拉伸
//        [image drawInRect:CGRectMake(10, 50, 300, 450)];
    //平鋪繪制
//        [image drawAsPatternInRect:CGRectMake(0, 0, 320, 568)];
}
#pragma mark 圖形上下文形變
-(void)_drawImageCTM:(CGContextRef)context
{
    //保存初始狀態(tài)
    CGContextSaveGState(context);
    //形變第一步:圖形上下文向右平移 X = 100
    CGContextTranslateCTM(context, 100, 20);
    //形變第二步:縮放0.8
    CGContextScaleCTM(context, 0.5, 0.5);
    //形變第三步:旋轉(zhuǎn)
    CGContextRotateCTM(context, M_PI_4/4);
    UIImage *image=[UIImage imageNamed:@"8"];
    [image drawInRect:CGRectMake(0, 50, 240, 300)];
    //恢復(fù)到初始狀態(tài)
    CGContextRestoreGState(context);
}
點(diǎn)擊屏幕時(shí),把控件的View截屏
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    把View的內(nèi)容截屏生成一張新的圖片.
    開啟一個(gè)跟控制器view相同大小的上下文.
    UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);

    把View的內(nèi)容繪制到上下文當(dāng)中.
    注意:View是不能夠直接繪制到上下文當(dāng)中的.View之所以能夠顯示是因?yàn)樗鼉?nèi)部有一個(gè)layer(層),
    層是通過(guò)渲染的方式繪制到上下文當(dāng)中的.
    獲取當(dāng)前的上下文.
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    把當(dāng)前View的內(nèi)容渲染到View上面.
    [self.view.layer renderInContext:ctx];
    從上下文當(dāng)中生成一張圖片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    關(guān)閉上下文.
    UIGraphicsEndImageContext();
    把生成的圖片寫到桌面上.
    桌面都是以流的形式傳遞數(shù)據(jù),所以我們要把圖片轉(zhuǎn)成二進(jìn)流.
    image:要轉(zhuǎn)的圖片
    compressionQuality:壓縮質(zhì)量,1代表質(zhì)量最高
    NSData *data = UIImageJPEGRepresentation(newImage, 1);
    原始質(zhì)量的png圖片.
    NSData *data = UIImagePNGRepresentation(newImage);
    把二進(jìn)流寫到桌面.
    [data writeToFile:@"路徑" atomically:YES];
}

本文主要以具體的實(shí)例講解如何繪制

1, 隨筆涂鴉

有兩方案處理:
方案1: 直接在 - (void)drawRect:(CGRect)rect{} 中繪制需要的圖形,因?yàn)槔锩嬉呀?jīng)有了上下文
方案2: 使用自己創(chuàng)建上下文的方式

大體的思路如下:
創(chuàng)建繼承自UIView的 IJSIPanDrawingView, 添加手勢(shì)并實(shí)現(xiàn)手勢(shì)方法
創(chuàng)建 IJSIPath對(duì)象 主要存儲(chǔ) 路徑的基本信息
手勢(shì)方法:

- (void)drawingViewDidPan:(UIPanGestureRecognizer*)sender
{
    CGPoint currentDraggingPosition = [sender locationInView:self.drawingView]; //獲取到的是手指點(diǎn)擊屏幕實(shí)時(shí)的坐標(biāo)點(diǎn)
    if(sender.state == UIGestureRecognizerStateBegan)  //一個(gè)手勢(shì)已經(jīng)開始但尚未改變或者完成時(shí)
    {
        // 初始化一個(gè)UIBezierPath對(duì)象, 把起始點(diǎn)存儲(chǔ)到UIBezierPath對(duì)象中, 用來(lái)存儲(chǔ)所有的軌跡點(diǎn)
        IJSIPath *path = [IJSIPath pathToPoint:currentDraggingPosition pathWidth: self.editorController.panWidth != 0 ? self.editorController.panWidth:   MAX(1, 4)];

        path.pathColor = self.editorController.panColor != nil ? self.editorController.panColor : [UIColor redColor];
        path.shape.strokeColor =[UIColor greenColor].CGColor; //代表設(shè)置它的邊框色
        [self.allLineArr addObject:path];   //添加路線
    }

    if(sender.state == UIGestureRecognizerStateChanged) //手勢(shì)狀態(tài)改變
    {
        // 獲得數(shù)組中的最后一個(gè)UIBezierPath對(duì)象(因?yàn)槲覀兠看味及裊IBezierPath存入到數(shù)組最后一個(gè),因此獲取時(shí)也取最后一個(gè))
        IJSIPath *path = [self.allLineArr lastObject];
        [path pathLineToPoint:currentDraggingPosition];//添加點(diǎn)
        [self drawLine];
        if (self.panDrawingViewdrawingCallBack) self.panDrawingViewdrawingCallBack(YES);
    }
}

滑動(dòng)開始 創(chuàng)建 allLineArr 數(shù)組 IJSIPath對(duì)象把所有的路徑信息保存到路徑數(shù)組中
滑動(dòng)進(jìn)行中去除數(shù)組最后一個(gè) IJSIPath對(duì)象把正在進(jìn)行的所有的點(diǎn)添加IJSIPath對(duì)象里面
核心繪制方法:

- (void)drawLine
{
    CGSize size = self.drawingView.frame.size;   //獲取繪制的大小
    UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); //創(chuàng)建一個(gè)基于位圖的上下文/NO 設(shè)置透明
    CGContextRef context = UIGraphicsGetCurrentContext();    // 獲取當(dāng)前上下文
    CGContextSetAllowsAntialiasing(context, true);        //去掉鋸齒
    CGContextSetShouldAntialias(context, true);
    for (IJSIPath *path in self.allLineArr)
    {
        [path drawPath];
    }
    self.drawingView.image = UIGraphicsGetImageFromCurrentImageContext();   //生成一個(gè)image對(duì)象
    UIGraphicsEndImageContext();
}
- (void)drawPath
{
    [self.pathColor set];   //填充顏色
    [self.bezierPath stroke];  // 根據(jù)坐標(biāo)點(diǎn)連線
}

2, 貼圖,或者文字

繪制文字和圖片比較簡(jiǎn)單,我們采用 UIKit為我們封裝的方法
比如: 首選選取圖片添加到 self.view子視圖,接下來(lái)我們要做的就是遍歷所有的子視圖并繪制

文字繪制:

- (void)drawRect:(CGRect)rect
{
    NSString *name = @"繪制文字繪制文字繪制文字繪制文字繪制文字";
    NSMutableDictionary *dic =[NSMutableDictionary dictionary];
    dic[NSFontAttributeName] = [UIFont systemFontOfSize:40];   // 字號(hào)
    NSShadow *shadow = [[NSShadow alloc]init];
    shadow.shadowOffset = CGSizeMake(10, 20); // 偏移量
    shadow.shadowBlurRadius = 2;  // 模糊度
    shadow.shadowColor = [UIColor blueColor];
    dic[NSShadowAttributeName] =shadow;
    // 繪制方法1:  自動(dòng)換行
//    [name drawInRect:CGRectMake(0, 0, rect.size.width, rect.size.height) withAttributes:dic];
    // 繪制方法2:   繪制不會(huì)換行
    [name drawAtPoint:CGPointZero withAttributes:dic];
}
    drawAtPoint:要畫到哪個(gè)位置
    withAttributes:文本的樣式.
    [str drawAtPoint:CGPointZero withAttributes:nil];

富文本中文字的注釋:

#import <Foundation/NSAttributedString.h>
字符屬性
 字符屬性可以應(yīng)用于 attributed string 的文本中。
 NSString *const NSFontAttributeName;(字體)
 NSString *const NSParagraphStyleAttributeName;(段落)
 NSString *const NSForegroundColorAttributeName;(字體顏色)
 NSString *const NSBackgroundColorAttributeName;(字體背景色)
 NSString *const NSLigatureAttributeName;(連字符)
 NSString *const NSKernAttributeName;(字間距)
 NSString *const NSStrikethroughStyleAttributeName;(刪除線)
 NSString *const NSUnderlineStyleAttributeName;(下劃線)
 NSString *const NSStrokeColorAttributeName;(邊線顏色)
 NSString *const NSStrokeWidthAttributeName;(邊線寬度)
 NSString *const NSShadowAttributeName;(陰影)(橫豎排版)
 NSString *const NSVerticalGlyphFormAttributeName;
 常量
 1> NSFontAttributeName(字體)
 該屬性所對(duì)應(yīng)的值是一個(gè) UIFont 對(duì)象擂仍。該屬性用于改變一段文本的字體囤屹。如果不指定該屬性,則默認(rèn)為12-point Helvetica(Neue)逢渔。
2> NSParagraphStyleAttributeName(段落)
 該屬性所對(duì)應(yīng)的值是一個(gè) NSParagraphStyle 對(duì)象肋坚。該屬性在一段文本上應(yīng)用多個(gè)屬性。如果不指定該屬性肃廓,則默認(rèn)為 NSParagraphStyle 的defaultParagraphStyle 方法返回的默認(rèn)段落屬性智厌。
 3> NSForegroundColorAttributeName(字體顏色)
 該屬性所對(duì)應(yīng)的值是一個(gè) UIColor 對(duì)象。該屬性用于指定一段文本的字體顏色盲赊。如果不指定該屬性铣鹏,則默認(rèn)為黑色。
 4> NSBackgroundColorAttributeName(字體背景色)
 該屬性所對(duì)應(yīng)的值是一個(gè) UIColor 對(duì)象哀蘑。該屬性用于指定一段文本的背景顏色诚卸。如果不指定該屬性,則默認(rèn)無(wú)背景色递礼。
 5> NSLigatureAttributeName(連字符)
 該屬性所對(duì)應(yīng)的值是一個(gè) NSNumber 對(duì)象(整數(shù))惨险。連體字符是指某些連在一起的字符羹幸,它們采用單個(gè)的圖元符號(hào)脊髓。0 表示沒(méi)有連體字符。1 表示使用默認(rèn)的連體字符栅受。2表示使用所有連體符號(hào)将硝。默認(rèn)值為 1(注意,iOS 不支持值為 2)屏镊。
 6> NSKernAttributeName(字間距)
 該屬性所對(duì)應(yīng)的值是一個(gè) NSNumber 對(duì)象(整數(shù))依疼。字母緊排指定了用于調(diào)整字距的像素點(diǎn)數(shù)。字母緊排的效果依賴于字體而芥。值為 0 表示不使用字母緊排律罢。默認(rèn)值為0。
 7> NSStrikethroughStyleAttributeName(刪除線)
該屬性所對(duì)應(yīng)的值是一個(gè) NSNumber 對(duì)象(整數(shù))。該值指定是否在文字上加上刪除線误辑,該值參考“Underline Style Attributes”沧踏。默認(rèn)值是NSUnderlineStyleNone。
 8> NSUnderlineStyleAttributeName(下劃線)
 該屬性所對(duì)應(yīng)的值是一個(gè) NSNumber 對(duì)象(整數(shù))巾钉。該值指定是否在文字上加上下劃線翘狱,該值參考“Underline Style Attributes”。默認(rèn)值是NSUnderlineStyleNone砰苍。
 9> NSStrokeColorAttributeName(邊線顏色)
 該屬性所對(duì)應(yīng)的值是一個(gè) UIColor 對(duì)象潦匈。如果該屬性不指定(默認(rèn)),則等同于 NSForegroundColorAttributeName赚导。否則茬缩,指定為刪除線或下劃線顏色。更多細(xì)節(jié)見(jiàn)“Drawing attributedstrings that are both filled and stroked”辟癌。
 10> NSStrokeWidthAttributeName(邊線寬度)
 該屬性所對(duì)應(yīng)的值是一個(gè) NSNumber 對(duì)象(小數(shù))寒屯。該值改變描邊寬度(相對(duì)于字體size 的百分比)。默認(rèn)為 0黍少,即不改變寡夹。正數(shù)只改變描邊寬度。負(fù)數(shù)同時(shí)改變文字的描邊和填充寬度厂置。例如菩掏,對(duì)于常見(jiàn)的空心字,這個(gè)值通常為3.0昵济。
11> NSShadowAttributeName(陰影)
  該屬性所對(duì)應(yīng)的值是一個(gè) NSShadow 對(duì)象智绸。默認(rèn)為 nil。
 12> NSVerticalGlyphFormAttributeName(橫豎排版)
 該屬性所對(duì)應(yīng)的值是一個(gè) NSNumber 對(duì)象(整數(shù))访忿。0 表示橫排文本瞧栗。1 表示豎排文本。在 iOS 中海铆,總是使用橫排文本迹恐,0 以外的值都未定義。

2,圖片繪制

圖片繪制和繪制文字一樣

- (void)drawRect:(CGRect)rect
{
    UIImage *image =[UIImage imageNamed:@"001.png"];
    UIRectClip(CGRectMake(10, 10, 50, 50));    // 裁剪,超過(guò)的區(qū)域?qū)⒉眉?    [image drawAtPoint:CGPointZero];    //繪制出來(lái)的圖圖片跟圖片的實(shí)際尺寸一樣大
    [image drawInRect:rect];     // 拉伸圖片,拉伸到指定的大小使用這個(gè)方法繪制出來(lái)的圖片尺寸會(huì)和傳入的rect區(qū)域一樣大.
    [image drawAsPatternInRect:rect];   // 平鋪
}

3, 高斯筆繪制

大體的思路就是: 獲取一張高斯圖,用戶獲取高斯圖的顏色作為涂鴉筆的顏色
首先獲取一張高斯圖

// 根據(jù)全圖獲取一張高斯模糊圖
- (UIImage *)getImageFilterForGaussianBlur:(int)blurNumber
{
    CGFloat blur = blurNumber * self.size.width / [UIScreen mainScreen].bounds.size.width;
    CIContext *context = [CIContext contextWithOptions:nil];
    CIImage *inputImage = [CIImage imageWithCGImage:self.CGImage];
    CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"
                                  keysAndValues:kCIInputImageKey, inputImage,
                        @"inputRadius", @(blur),
                        nil];
    CIImage *outputImage = filter.outputImage;
    return [UIImage imageWithCGImage:[context createCGImage:outputImage fromRect:CGRectMake(0, 0, self.size.width, self.size.height)]];
}

繪制過(guò)程和隨筆涂鴉相同區(qū)別只要在下面一句

CGContextSetStrokeColorWithColor(context, [UIColor colorWithPatternImage:self.gaussanViewGaussanImage].CGColor);

此處采用底層的實(shí)現(xiàn)方法

- (void)drawSmearView
{
    UIGraphicsBeginImageContext(self.originImage.size); // 開啟上下文
    CGContextRef context = UIGraphicsGetCurrentContext(); // 獲取當(dāng)前的上下
    CGContextSetLineCap(context, kCGLineCapRound); // 設(shè)置線尾的樣式
    [self.originImage drawInRect:CGRectMake(0, 0,self.originImage.size.width,self.originImage.size.height)]; // 繪制原圖用于地圖顯示
    
    CGContextSetStrokeColorWithColor(context, [UIColor colorWithPatternImage:self.gaussanViewGaussanImage].CGColor); //獲取高斯圖的顏色
    
    CGContextSetLineWidth(context, 10 * self.originImage.size.width / self.bounds.size.width); //線寬
    for (int i = 0 ; i < self.allLineArr.count ; i ++ ) {
        NSMutableArray *array = [self.allLineArr objectAtIndex:i];
        
        for (int i = 0 ; i < array.count ; i ++ ) {
            NSValue *value = [array objectAtIndex:i];
            CGPoint p = [value CGPointValue];
            p.x = p.x * self.originImage.size.width / self.bounds.size.width;
            p.y = p.y * self.originImage.size.height / self.bounds.size.height;
            if (i == 0) {
                CGContextMoveToPoint(context, p.x, p.y); // 設(shè)置起點(diǎn)
                CGContextAddLineToPoint(context, p.x, p.y); //添加移動(dòng)的點(diǎn)
            }else{
                CGContextAddLineToPoint(context, p.x, p.y);  
            }
        }
    }
    CGContextDrawPath(context, kCGPathStroke);  //將路徑繪制到上下問(wèn)
    
    // 將繪制的結(jié)果存儲(chǔ)在內(nèi)存中
    self.nowImage = UIGraphicsGetImageFromCurrentImageContext();
    
    // 結(jié)束繪制
    UIGraphicsEndImageContext();
    [self setNeedsDisplay]; //重繪制
}

3, 馬賽克繪制

思路: 刮刮卡
單獨(dú)的view,沒(méi)有底圖的情況下需要?jiǎng)?chuàng)建一個(gè) UIImageView 用于顯示原圖
單獨(dú)的view UI結(jié)構(gòu)如下:

  1. self.view 子視圖是 UIImageView 用于顯示沒(méi)有馬賽克的部分
  2. 創(chuàng)建 CALayer 添加到 self.layer,主要用于顯示馬賽克圖, 在這一層的 contents 中添加馬賽克的圖片
  3. 創(chuàng)建 CAShapeLayer 主要用與在手指滑動(dòng)時(shí)候顯示馬賽克路徑, CAShapeLayer是一個(gè)通過(guò)矢量圖形來(lái)繪制的圖層子類,可以實(shí)現(xiàn)不規(guī)則路徑的視圖顯示, 這里創(chuàng)建的 CAShapeLayer 只有大小沒(méi)有具體內(nèi)容,所以 CALayer 上的馬賽克視圖不會(huì)顯示

非常重要的屬性:

self.imageLayer.mask = self.shapeLayer;  // 子視圖完全遮蓋馬賽克視圖
mask將 self.shapeLayer; 設(shè)置成  self.imageLayer.mask 相當(dāng)于 給 self.imageLayer 添加了一層面罩,罩住的部分是顯示的,  沒(méi)有罩住的部分不顯示

代碼如下:
創(chuàng)建 CALayer CAShapeLayer

設(shè)置self.imageLayer.mask = self.shapeLayer;  // 子視圖完全遮蓋馬賽克視圖

移動(dòng)中中間的過(guò)程和之前的路徑移動(dòng)一樣 唯一的區(qū)別在于

#pragma mark 繪制
- (void)drawSmearView
{
    for (int i = 0 ; i < self.allLineArr.count ; i ++ )
    {
        NSMutableArray *array = [self.allLineArr objectAtIndex:i];
        for (int i = 0 ; i < array.count ; i ++ )
        {
            CGMutablePathRef path = (__bridge CGMutablePathRef)([array objectAtIndex:i]);
            self.shapeLayer.path = path; // 將移動(dòng)中 CGMutablePathRef 路徑設(shè)置給  CAShapeLayer 可以產(chǎn)生一個(gè)不規(guī)則的路徑,并且是透明空的
        }
    }
}

到此大功告成

關(guān)于馬賽克原理的介紹:

其實(shí)給圖片打碼并不是在原有的圖片上添加一層“蒙版”卧斟,而是使用各個(gè)平臺(tái)提供的API去操作像素點(diǎn)殴边,認(rèn)為的干擾了像素點(diǎn),就實(shí)現(xiàn)了馬賽克的效果珍语,以下面兩幅圖為例子锤岸,介紹一下如何的去“干擾像素”。

    在圖像學(xué)中板乙,如果你想去對(duì)圖片進(jìn)行處理是偷,就必須得知道一個(gè)概念什么是“位圖圖像”。位圖圖像(bitmap), 亦稱為點(diǎn)陣圖像或繪制圖像,是由稱作像素(圖片元素)的單個(gè)點(diǎn)組成的蛋铆。這些點(diǎn)可以進(jìn)行不同的排列和染色以構(gòu)成圖樣饿幅。當(dāng)放大位圖時(shí),可以看見(jiàn)賴以構(gòu)成整個(gè)圖像的無(wú)數(shù)單個(gè)方塊戒职。擴(kuò)大位圖尺寸的效果是增大單個(gè)像素栗恩,從而使線條和形狀顯得參差不齊。然而洪燥,如果從稍遠(yuǎn)的位置觀看它磕秤,位圖圖像的顏色和形狀又顯得是連續(xù)的

圖解:
1, 假如一個(gè)圖像是由6 * 9 = 72個(gè)像素組成,現(xiàn)將一個(gè)像素點(diǎn)放大到圖1方塊單位大小捧韵。

馬賽克原理.png

2, 坐標(biāo)系
現(xiàn)在以左下角第一個(gè)方塊為原點(diǎn)將圖像納入坐標(biāo)系中市咆,如下圖所示。馬賽克效果實(shí)際上是在原始圖片的起始位置(0再来,8)到(2蒙兰,6)其中包含了9個(gè)像素(馬賽克矩形3 * 3)。

馬賽克.png

馬賽克矩形:
這里所說(shuō)的馬賽克矩形芒篷,指的是N個(gè)三位像素所組成的矩形(這里使用3*3)搜变,使每一個(gè)矩形的ARGB都和第一個(gè)矩形的ARGB相同,就達(dá)到了破壞原有圖像的效果

代碼

- (UIImage *)getMosaicImageFromOrginImageBlockLevel:(NSUInteger)level
{
    // self == OrginImage
    //1,獲取BitmapData
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();  // 創(chuàng)建顏色空間
    CGImageRef imgRef = self.CGImage;   // 圖片轉(zhuǎn)換
    CGFloat width = CGImageGetWidth(imgRef);  //圖片寬
    CGFloat height = CGImageGetHeight(imgRef); //高
   
    // 2, 創(chuàng)建圖片上下文(解析圖片信息针炉,繪制圖片 開辟內(nèi)存空間挠他,這塊空間用于處理馬賽克圖片
    CGContextRef context = CGBitmapContextCreate (nil,  //數(shù)據(jù)源
                                                  width,
                                                  height,
                                                  kBitsPerComponent,  //每個(gè)顏色值8bit,圖像學(xué)中,像素點(diǎn):ARGB組成 每一個(gè)表示一個(gè)分量(例如A,R,G,B),
                                                  width*kPixelChannelCount, //每一行的像素點(diǎn)占用的字節(jié)數(shù),每個(gè)像素點(diǎn)的ARGB四個(gè)通道各占8個(gè)bit
                                                  colorSpace,  // 顏色空間
                                                  kCGImageAlphaPremultipliedLast); //是否需要透明度
    // 3, 根據(jù)圖片上下文繪制圖片
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imgRef);
    
    // 4, 獲取圖片的像素?cái)?shù)組
    unsigned char *bitmapData = CGBitmapContextGetData (context);
    
    //5, 核心算法 圖片打碼,加入馬賽克,這里把BitmapData進(jìn)行馬賽克轉(zhuǎn)換,讓一個(gè)像素點(diǎn)替換為和它相同的矩形區(qū)域(正方形殖侵,圓形都可以)
    unsigned char pixel[kPixelChannelCount] = {0}; // 像素點(diǎn)默認(rèn)是4個(gè)通道,默認(rèn)值是0
    NSUInteger index,preIndex;
    for (NSUInteger i = 0; i < height - 1 ; i++)
    {
        for (NSUInteger j = 0; j < width - 1; j++)
        {
            index = i * width + j;  // 獲取當(dāng)前像素點(diǎn)坐標(biāo)
            if (i % level == 0)
            {
                if (j % level == 0)
                {
                    memcpy(pixel, bitmapData + kPixelChannelCount*index, kPixelChannelCount); //給我們的像素點(diǎn)賦值
                }else{
                    memcpy(bitmapData + kPixelChannelCount*index, pixel, kPixelChannelCount);
                }
            } else {
                preIndex = (i-1)*width +j;
                memcpy(bitmapData + kPixelChannelCount*index, bitmapData + kPixelChannelCount*preIndex, kPixelChannelCount);
            }
        }
    }
    
    // 6, 獲取圖片數(shù)據(jù)集合
    NSInteger dataLength = width*height* kPixelChannelCount;
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bitmapData, dataLength, NULL);
    
    //7, 創(chuàng)建要輸出的圖像
    CGImageRef mosaicImageRef = CGImageCreate(width, height,
                                              kBitsPerComponent,  //表示每一個(gè)像素點(diǎn),每一個(gè)分量的大小
                                              kBitsPerPixel,   //每一個(gè)像素點(diǎn)的大小
                                              width*kPixelChannelCount ,  //每一行內(nèi)存大小
                                              colorSpace,      //顏色空間
                                              kCGBitmapByteOrderDefault,  //位圖信息
                                              provider,      //數(shù)據(jù)源(數(shù)據(jù)集合)
                                              NULL,         //數(shù)據(jù)解碼器
                                              NO,             // 是否抗鋸齒
                                              kCGRenderingIntentDefault);   //渲染器
    
    // 8 創(chuàng)建輸出馬賽克圖片(填充顏色)
    CGContextRef outputContext = CGBitmapContextCreate(nil,
                                                       width,
                                                       height,
                                                       kBitsPerComponent,
                                                       width*kPixelChannelCount,
                                                       colorSpace,
                                                       kCGImageAlphaPremultipliedLast);
    CGContextDrawImage(outputContext, CGRectMake(0.0f, 0.0f, width, height), mosaicImageRef); //  //繪制圖片
    CGImageRef resultImageRef = CGBitmapContextCreateImage(outputContext);  // //創(chuàng)建圖片
    UIImage *resultImage = nil;
    if([UIImage respondsToSelector:@selector(imageWithCGImage:scale:orientation:)])
    {
        float scale = [[UIScreen mainScreen] scale];
        resultImage = [UIImage imageWithCGImage:resultImageRef scale:scale orientation:UIImageOrientationUp];
    } else {
        resultImage = [UIImage imageWithCGImage:resultImageRef];
    }
    //釋放
    if(resultImageRef)
    {
        CFRelease(resultImageRef);
    }
    if(mosaicImageRef)
    {
        CFRelease(mosaicImageRef);
    }
    if(colorSpace)
    {
        CGColorSpaceRelease(colorSpace);
    }
    if(provider)
    {
        CGDataProviderRelease(provider);
    }
    if(context)
    {
        CGContextRelease(context);
    }
    if(outputContext)
    {
        CGContextRelease(outputContext);
    }
    return resultImage;
}

ios圖像知識(shí)

1,什么是圖形圖像

一張圖像就是像素點(diǎn)的集合镰烧,每一個(gè)像素都是一個(gè)單獨(dú)拢军,明了的顏色。圖像一般情況下都存儲(chǔ)成數(shù)組怔鳖,你可以把他們相像成2維數(shù)組茉唉。

這一張是縮放版本的幽靈,被放大后:

圖像中這些小的“方塊”就是像素败砂,每一像素只表示一種顏色赌渣。當(dāng)成百上千萬(wàn)的像素集體到一起后魏铅,就構(gòu)成了圖形圖像昌犹。

如何用字節(jié)來(lái)表示顏色

表示圖形的方式有許多種。在本教程中使用的是最簡(jiǎn)單的:32位RGBA模式览芳。

如同它的名字一樣斜姥,32位 RGBA 模式會(huì)將一個(gè)顏色值存儲(chǔ)在32位,或者4個(gè)字節(jié)中。每一個(gè)字節(jié)存儲(chǔ)一個(gè)部分或者一個(gè)顏色通道铸敏。這4個(gè)部分分別是:

~ R代表紅色

~ G代表綠色

~ B代表藍(lán)色

~ A代表透明度

正如你所知道的缚忧,紅,綠和藍(lán)是所有顏色的基本顏色集杈笔。你幾乎可以使用他們創(chuàng)建搭配出任何想要的顏色闪水。

由于使用8位表示每一種顏色值,那么使用32位RGBA模式實(shí)際上可以創(chuàng)建出不透明的顏色的總數(shù)是256256256種蒙具,已經(jīng)接近17億種球榆。驚嘆,那是好多好多好多的顏色禁筏!

alpha通道與其它的不同持钉。你可以把它當(dāng)成透明的東西,就像UIView的alpah屬性篱昔。

透明顏色意味著沒(méi)有任何的顏色每强,除非在它的后面有另外一種顏色;它的主要功能就是要告訴圖像處理這個(gè)像素的透明度是多少州刽,于是空执,就會(huì)有多少顏色值穿透過(guò)它而顯示出來(lái)。

你將會(huì)通過(guò)本節(jié)后面的內(nèi)容更新深入的了解穗椅。

總結(jié)一下脆烟,一個(gè)圖形就是像素的集體,并且每一個(gè)像素只能表示一種顏色房待。本節(jié)邢羔,你已經(jīng)了解了32位RGBA模式。

提示:你有沒(méi)有想過(guò)桑孩,位圖的結(jié)構(gòu)組成拜鹤?一張位圖就是一張2D的地圖,每一塊就是一個(gè)像素流椒!像素就是地圖的每一塊敏簿。

現(xiàn)在你已經(jīng)了解了用字節(jié)表示顏色的基礎(chǔ)了。不過(guò)在你開始著手寫代碼前宣虾,還有三個(gè)以上的概念需要你了解惯裕。

2,顏色空間

使用RGB模式表示顏色是顏色空間的一個(gè)例子。它只是眾多存儲(chǔ)顏色方法中的一種绣硝。另外一種顏色空間是灰階空間蜻势。像它的名字一樣,所有的圖形都只有黑和白鹉胖,只需要保存一個(gè)值來(lái)表示這種顏色握玛。

下面這種使用RGB模式表示的顏色够傍,人類的肉眼是很難識(shí)別的。

Red: 0 Green:104 Blue:55

你認(rèn)為RGB值為[0,104,55]會(huì)產(chǎn)生一種什么顏色挠铲?

認(rèn)真的思考一下冕屯,你也許會(huì)說(shuō)是一種藍(lán)綠色或者綠色,但那是錯(cuò)的拂苹。原來(lái)安聘,你所看到的是深綠色。

另外兩種比較常見(jiàn)的顏色空間是HSV和YUV瓢棒。

HSV搞挣,使用色調(diào),飽和度和亮度來(lái)直觀的存儲(chǔ)顏色值音羞。你可以把這三個(gè)部分這樣來(lái)看:

·色調(diào)就是顏色
·飽和度就是這個(gè)顏色有多么的飽滿
·值就是顏色的亮度有多亮

在這種顏色空間中囱桨,如果你發(fā)現(xiàn)自己并不知道HSV的值,那么通過(guò)它的三個(gè)值嗅绰,可以很容易的相像出大概是什么顏色舍肠。

RGB和HSV顏色空間的區(qū)別是很容易理解的,請(qǐng)看下面的圖像:

YUV是另外一種常見(jiàn)的顏色空間窘面,電視機(jī)使用的就是這種方式翠语。

最開始的時(shí)候,電視機(jī)只有灰階空間一種顏色通道财边。后來(lái)肌括,當(dāng)彩色電影出現(xiàn)后,就有了2種通道酣难。當(dāng)然谍夭,如果你想在本教程中使用YUV,那么你需要去研究更多關(guān)于YUV和其它顏色空間的相關(guān)知識(shí)憨募。

NOTE:同樣的顏色空間紧索,你也可以使用不同的方法表示顏色。比如16位RGB模式,可以使用5個(gè)字節(jié)存儲(chǔ)R,6個(gè)字節(jié)存儲(chǔ)G父款,5個(gè)字節(jié)存儲(chǔ)B。

為什么用6個(gè)字節(jié)存儲(chǔ)綠色媳危,5個(gè)字節(jié)存儲(chǔ)藍(lán)色?這是一個(gè)有意思的問(wèn)題冈敛,答案就是因?yàn)檠矍虼ΑH祟惖难矍驅(qū)G色比較敏感,所以人類的眼球更空間分辨出綠色的顏色值變化莺债。

3,坐標(biāo)系統(tǒng)

既然一個(gè)圖形是由像素構(gòu)成的平面地圖滋觉,那么圖像的原點(diǎn)需要說(shuō)明一下。通常原點(diǎn)在圖像的左上角齐邦,Y軸向下椎侠;或者原點(diǎn)在圖像的左下,Y軸向上措拇。

沒(méi)有固定的坐標(biāo)系統(tǒng)我纪,蘋果在不同的地方可能會(huì)使用不同的坐標(biāo)系。

目前丐吓,UIImage和UIView使用的是左上原點(diǎn)坐標(biāo)浅悉,Core Image和Core Graphics使用的是左下原點(diǎn)坐標(biāo)。這個(gè)概念很重要券犁,當(dāng)你遇到圖像繪制倒立問(wèn)題的時(shí)候你就知道了术健。

4,圖形壓縮

這是在你開始編寫代碼前的最后一個(gè)需要了解的概念了!原圖的每一個(gè)像素都被存儲(chǔ)在各自的內(nèi)存中粘衬。

如果你使用一張8像素的圖形做運(yùn)算荞估,它將會(huì)消耗810^6像素4比特/像素=32兆字節(jié)內(nèi)存。關(guān)注一下數(shù)據(jù)稚新!

這就是為什么會(huì)出現(xiàn)jpeg,png和其它圖形格式的原因勘伺。這些都是圖形壓縮格式。

當(dāng)GPU在繪制圖像的時(shí)候褂删,會(huì)使用大量?jī)?nèi)存把圖像的原始尺寸進(jìn)行解壓縮飞醉。如果你的程序占用了過(guò)多的內(nèi)存,那么操作系統(tǒng)會(huì)將進(jìn)程殺死(程序崩潰)屯阀。所以請(qǐng)確定你的程序使用較大的圖像進(jìn)行過(guò)測(cè)試缅帘。

關(guān)注一下像素

圖片加載到你的手機(jī)上會(huì)看到如下的圖像:

在控制臺(tái),你會(huì)看到如下的輸出:

當(dāng)前的程序可以加載這張幽靈的圖像难衰,并得到圖像的所有像素值股毫,打印出每個(gè)像素的亮度值到日志中。

亮度值是神馬召衔?它就是紅色铃诬,綠色和藍(lán)色通過(guò)的平均值。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末苍凛,一起剝皮案震驚了整個(gè)濱河市趣席,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌醇蝴,老刑警劉巖宣肚,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異悠栓,居然都是意外死亡霉涨,警方通過(guò)查閱死者的電腦和手機(jī)按价,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)笙瑟,“玉大人楼镐,你說(shuō)我怎么就攤上這事⊥希” “怎么了框产?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)错洁。 經(jīng)常有香客問(wèn)我秉宿,道長(zhǎng),這世上最難降的妖魔是什么屯碴? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任描睦,我火速辦了婚禮,結(jié)果婚禮上导而,老公的妹妹穿的比我還像新娘酌摇。我一直安慰自己,他們只是感情好嗡载,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布窑多。 她就那樣靜靜地躺著,像睡著了一般洼滚。 火紅的嫁衣襯著肌膚如雪埂息。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天遥巴,我揣著相機(jī)與錄音千康,去河邊找鬼。 笑死铲掐,一個(gè)胖子當(dāng)著我的面吹牛拾弃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播摆霉,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼豪椿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了携栋?” 一聲冷哼從身側(cè)響起搭盾,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎婉支,沒(méi)想到半個(gè)月后鸯隅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡向挖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年蝌以,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了炕舵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡跟畅,死狀恐怖咽筋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情碍彭,我是刑警寧澤晤硕,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布悼潭,位于F島的核電站,受9級(jí)特大地震影響皆疹,放射性物質(zhì)發(fā)生泄漏占拍。R本人自食惡果不足惜略就,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一晃酒、第九天 我趴在偏房一處隱蔽的房頂上張望表牢。 院中可真熱鬧崔兴,春花似錦蛔翅、人聲如沸敲茄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)堰燎。三九已至,卻和暖如春笋轨,著一層夾襖步出監(jiān)牢的瞬間秆剪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工爵政, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鸟款,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓茂卦,卻偏偏與公主長(zhǎng)得像何什,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子等龙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容