SLMosaicView.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. //
  2. // SLMosaicView.m
  3. // DarkMode
  4. //
  5. // Created by wsl on 2019/10/25.
  6. // Copyright © 2019 wsl. All rights reserved.
  7. //
  8. #import "SLMosaicView.h"
  9. #define radiansToDegrees(x) (180.0 * x / M_PI)
  10. ///两点之间的角度
  11. CGFloat angleBetweenPoints(CGPoint startPoint, CGPoint endPoint) {
  12. CGPoint Xpoint = CGPointMake(startPoint.x + 100, startPoint.y);
  13. CGFloat a = endPoint.x - startPoint.x;
  14. CGFloat b = endPoint.y - startPoint.y;
  15. CGFloat c = Xpoint.x - startPoint.x;
  16. CGFloat d = Xpoint.y - startPoint.y;
  17. CGFloat rads = acos(((a*c) + (b*d)) / ((sqrt(a*a + b*b)) * (sqrt(c*c + d*d))));
  18. if (startPoint.y>endPoint.y) {
  19. rads = -rads;
  20. }
  21. return rads;
  22. }
  23. ///直线之间的夹角
  24. CGFloat angleBetweenLines(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {
  25. CGFloat a = line1End.x - line1Start.x;
  26. CGFloat b = line1End.y - line1Start.y;
  27. CGFloat c = line2End.x - line2Start.x;
  28. CGFloat d = line2End.y - line2Start.y;
  29. CGFloat rads = acos(((a*c) + (b*d)) / ((sqrt(a*a + b*b)) * (sqrt(c*c + d*d))));
  30. return radiansToDegrees(rads);
  31. }
  32. /// 马赛克点元素
  33. @interface SLMosaicPointElement : NSObject
  34. @property (nonatomic, assign) CGRect rect;
  35. @property (nonatomic, strong) UIColor *color;
  36. @property (nonatomic, copy) NSString *imageName;
  37. @end
  38. @implementation SLMosaicPointElement
  39. @end
  40. /// 马赛克线条 遮罩层 线
  41. @interface SLMosaicLineLayer : CALayer
  42. @property (nonatomic, strong) NSMutableArray <SLMosaicPointElement *>*elementArray;
  43. @end
  44. @implementation SLMosaicLineLayer
  45. - (instancetype)init {
  46. self = [super init];
  47. if (self) {
  48. self.contentsScale = [[UIScreen mainScreen] scale];
  49. self.backgroundColor = [UIColor clearColor].CGColor;
  50. _elementArray = [@[] mutableCopy];
  51. }
  52. return self;
  53. }
  54. - (void)drawInContext:(CGContextRef)context {
  55. UIGraphicsPushContext( context );
  56. [[UIColor clearColor] setFill];
  57. UIRectFill(self.bounds);
  58. CGContextSetLineCap(context, kCGLineCapRound);
  59. CGContextSetLineJoin(context, kCGLineJoinRound);
  60. for (NSInteger i=0; i<self.elementArray.count; i++) {
  61. SLMosaicPointElement *blur = self.elementArray[i];
  62. CGRect rect = blur.rect;
  63. if (blur.imageName) {
  64. UIImage *image = [UIImage imageNamed:blur.imageName];
  65. if (image) {
  66. /** 创建颜色图片 */
  67. CGColorSpaceRef colorRef = CGColorSpaceCreateDeviceRGB();
  68. CGContextRef contextRef = CGBitmapContextCreate(nil, image.size.width, image.size.height, 8, 0, colorRef, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
  69. CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
  70. CGContextClipToMask(contextRef, imageRect, image.CGImage);
  71. CGContextSetFillColorWithColor(contextRef, (blur.color ? blur.color.CGColor : [UIColor clearColor].CGColor));
  72. CGContextFillRect(contextRef,imageRect);
  73. /** 生成图片 */
  74. CGImageRef imageRef = CGBitmapContextCreateImage(contextRef);
  75. CGContextDrawImage(context, rect, imageRef);
  76. CGImageRelease(imageRef);
  77. CGContextRelease(contextRef);
  78. CGColorSpaceRelease(colorRef);
  79. }
  80. } else {
  81. // 设置描边颜色
  82. // CGContextSetStrokeColorWithColor(context, (blur.color ? blur.color.CGColor : [UIColor clearColor].CGColor));
  83. // 模糊矩形可以用到,用于填充矩形
  84. CGContextSetFillColorWithColor(context, (blur.color ? blur.color.CGColor : [UIColor clearColor].CGColor));
  85. // 模糊矩形 填充 画完一个小正方形
  86. CGContextFillRect(context, rect);
  87. }
  88. }
  89. UIGraphicsPopContext();
  90. }
  91. @end
  92. NSString *const kLFSplashViewData = @"LFSplashViewData";
  93. NSString *const kLFSplashViewData_layerArray = @"LFSplashViewData_layerArray";
  94. NSString *const kLFSplashViewData_frameArray = @"LFSplashViewData_frameArray";
  95. /// 马赛克画板
  96. @interface SLMosaicView ()
  97. {
  98. BOOL _isWork;
  99. BOOL _isBegan;
  100. }
  101. /** 图层 */
  102. @property (nonatomic, strong) NSMutableArray <SLMosaicLineLayer *>*layerArray;
  103. /** 已显示坐标 */
  104. @property (nonatomic, strong) NSMutableArray <NSValue *>*frameArray;
  105. //@property (nonatomic, assign) BOOL isErase;
  106. @end
  107. @implementation SLMosaicView
  108. - (instancetype)initWithFrame:(CGRect)frame {
  109. self = [super initWithFrame:frame];
  110. if (self) {
  111. [self customInit];
  112. }
  113. return self;
  114. }
  115. #pragma mark - Help Methods
  116. //初始化
  117. - (void)customInit {
  118. _squareWidth = 15.f;
  119. _paintSize = CGSizeMake(50, 50);
  120. _mosaicType = SLMosaicTypeSquare;
  121. _layerArray = [@[] mutableCopy];
  122. _frameArray = [@[] mutableCopy];
  123. }
  124. //返回马赛克元素的位置
  125. - (CGPoint)divideMosaicPoint:(CGPoint)point {
  126. CGFloat scope = self.squareWidth;
  127. int x = point.x/scope;
  128. int y = point.y/scope;
  129. return CGPointMake(x*scope, y*scope);
  130. }
  131. //马赛克元素在设备上的区域
  132. - (NSArray <NSValue *>*)divideMosaicRect:(CGRect)rect {
  133. CGFloat scope = self.squareWidth;
  134. NSMutableArray *array = @[].mutableCopy;
  135. if (CGRectEqualToRect(CGRectZero, rect)) {
  136. return array;
  137. }
  138. CGFloat minX = CGRectGetMinX(rect);
  139. CGFloat maxX = CGRectGetMaxX(rect);
  140. CGFloat minY = CGRectGetMinY(rect);
  141. CGFloat maxY = CGRectGetMaxY(rect);
  142. /** 左上角 */
  143. CGPoint leftTop = [self divideMosaicPoint:CGPointMake(minX, minY)];
  144. /** 右下角 */
  145. CGPoint rightBoom = [self divideMosaicPoint:CGPointMake(maxX, maxY)];
  146. NSInteger countX = (rightBoom.x - leftTop.x)/scope;
  147. NSInteger countY = (rightBoom.y - leftTop.y)/scope;
  148. for (NSInteger i = 0; i < countX; i++) {
  149. for (NSInteger j = 0; j < countY; j++) {
  150. CGPoint point = CGPointMake(leftTop.x + i * scope, leftTop.y + j * scope);
  151. NSValue *value = [NSValue valueWithCGPoint:point];
  152. [array addObject:value];
  153. }
  154. }
  155. return array;
  156. }
  157. #pragma mark - 绘画
  158. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  159. if (touches.allObjects.count == 1) {
  160. _isWork = NO;
  161. _isBegan = YES;
  162. //1、触摸坐标
  163. UITouch *touch = [touches anyObject];
  164. CGPoint point = [touch locationInView:self];
  165. //2、创建LFSplashBlur
  166. if (self.mosaicType == SLMosaicTypeSquare) {
  167. CGPoint mosaicPoint = [self divideMosaicPoint:point];
  168. NSValue *value = [NSValue valueWithCGPoint:mosaicPoint];
  169. if (![self.frameArray containsObject:value]) {
  170. [self.frameArray addObject:value];
  171. SLMosaicPointElement *blur = [SLMosaicPointElement new];
  172. blur.rect = CGRectMake(mosaicPoint.x, mosaicPoint.y, self.squareWidth, self.squareWidth);
  173. blur.color = self.brushColor ? self.brushColor(blur.rect.origin) : nil;
  174. SLMosaicLineLayer *layer = [SLMosaicLineLayer layer];
  175. layer.frame = self.bounds;
  176. [layer.elementArray addObject:blur];
  177. [self.layer addSublayer:layer];
  178. [self.layerArray addObject:layer];
  179. } else {
  180. SLMosaicLineLayer *layer = [SLMosaicLineLayer layer];
  181. layer.frame = self.bounds;
  182. [self.layer addSublayer:layer];
  183. [self.layerArray addObject:layer];
  184. }
  185. } else if (self.mosaicType == SLMosaicTypePaintbrush) {
  186. SLMosaicPointElement *blur = [ SLMosaicPointElement new];
  187. blur.rect = CGRectMake(point.x-self.paintSize.width/2, point.y-self.paintSize.height/2, self.paintSize.width, self.paintSize.height);
  188. blur.imageName = @"EditMosaicBrush.png";
  189. blur.color = self.brushColor ? self.brushColor(blur.rect.origin) : nil;
  190. SLMosaicLineLayer *layer = [SLMosaicLineLayer layer];
  191. layer.frame = self.bounds;
  192. [layer.elementArray addObject:blur];
  193. [self.layer addSublayer:layer];
  194. [self.layerArray addObject:layer];
  195. }
  196. }
  197. [super touchesBegan:touches withEvent:event];
  198. }
  199. - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  200. if (_isBegan || _isWork) {
  201. //1、触摸坐标
  202. UITouch *touch = [touches anyObject];
  203. CGPoint point = [touch locationInView:self];
  204. /** 获取上一个对象坐标判断是否重叠 */
  205. SLMosaicLineLayer *layer = self.layerArray.lastObject;
  206. SLMosaicPointElement *prevBlur = layer.elementArray.lastObject;
  207. if (self.mosaicType == SLMosaicTypeSquare) {
  208. CGPoint mosaicPoint = [self divideMosaicPoint:point];
  209. NSValue *value = [NSValue valueWithCGPoint:mosaicPoint];
  210. if (![self.frameArray containsObject:value]) {
  211. if (_isBegan && self.brushBegan) self.brushBegan();
  212. _isWork = YES;
  213. _isBegan = NO;
  214. [self.frameArray addObject:value];
  215. //2、创建LFSplashBlur
  216. SLMosaicPointElement *blur = [SLMosaicPointElement new];
  217. blur.rect = CGRectMake(mosaicPoint.x, mosaicPoint.y, self.squareWidth, self.squareWidth);
  218. blur.color = self.brushColor ? self.brushColor(blur.rect.origin) : nil;
  219. [layer.elementArray addObject:blur];
  220. [layer setNeedsDisplay];
  221. }
  222. } else if (self.mosaicType == SLMosaicTypePaintbrush) {
  223. /** 限制绘画的间隙 */
  224. if (CGRectContainsPoint(prevBlur.rect, point) == NO) {
  225. if (_isBegan && self.brushBegan) self.brushBegan();
  226. _isWork = YES;
  227. _isBegan = NO;
  228. //2、创建LFSplashBlur
  229. SLMosaicPointElement *blur = [SLMosaicPointElement new];
  230. blur.imageName = @"EditMosaicBrush.png";
  231. blur.color = self.brushColor ? self.brushColor(point) : nil;
  232. /** 新增随机位置 */
  233. int x = self.paintSize.width + MIN(1, (int)(self.paintSize.width*0.4));
  234. float randomX = floorf(arc4random()%x) - x/2;
  235. blur.rect = CGRectMake(point.x-self.paintSize.width/2 + randomX, point.y-self.paintSize.height/2, self.paintSize.width, self.paintSize.height);
  236. [layer.elementArray addObject:blur];
  237. /** 新增额外对象 密集图片 */
  238. [layer setNeedsDisplay];
  239. /** 扩大范围 */
  240. CGRect paintRect = CGRectInset(blur.rect, -self.squareWidth, -self.squareWidth);
  241. [self.frameArray removeObjectsInArray:[self divideMosaicRect:paintRect]];
  242. }
  243. }
  244. }
  245. [super touchesMoved:touches withEvent:event];
  246. }
  247. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  248. if (_isWork) {
  249. SLMosaicLineLayer *layer = self.layerArray.lastObject;
  250. if (layer.elementArray.count < 2) {
  251. [self goBack];
  252. } else {
  253. if (self.brushEnded) self.brushEnded();
  254. }
  255. } else {
  256. if ((_isBegan)) {
  257. [self goBack];
  258. }
  259. }
  260. _isBegan = NO;
  261. _isWork = NO;
  262. [super touchesEnded:touches withEvent:event];
  263. }
  264. - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  265. if (_isWork) {
  266. SLMosaicLineLayer *layer = self.layerArray.lastObject;
  267. if (layer.elementArray.count < 2) {
  268. [self goBack];
  269. } else {
  270. if (self.brushEnded) self.brushEnded();
  271. }
  272. } else {
  273. if ((_isBegan)) {
  274. [self goBack];
  275. }
  276. }
  277. _isBegan = NO;
  278. _isWork = NO;
  279. [super touchesCancelled:touches withEvent:event];
  280. }
  281. - (BOOL)isDrawing {
  282. return _isWork;
  283. }
  284. /// 是否可撤销
  285. - (BOOL)canBack {
  286. return self.layerArray.count;
  287. }
  288. //撤销
  289. - (void)goBack {
  290. if (!self.canBack) {
  291. return;
  292. }
  293. SLMosaicLineLayer *layer = self.layerArray.lastObject;
  294. if ([layer.elementArray.firstObject isMemberOfClass:[SLMosaicPointElement class]]) {
  295. for (SLMosaicPointElement *blur in layer.elementArray) {
  296. [self.frameArray removeObject:[NSValue valueWithCGPoint:blur.rect.origin]];
  297. }
  298. }
  299. [layer removeFromSuperlayer];
  300. [self.layerArray removeLastObject];
  301. }
  302. - (void)clear {
  303. [self.layerArray removeAllObjects];
  304. [self.frameArray removeAllObjects];
  305. [self.layer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];
  306. }
  307. #pragma mark - 数据
  308. - (NSDictionary *)data {
  309. if (self.layerArray.count) {
  310. NSMutableArray *lineArray = [@[] mutableCopy];
  311. for (SLMosaicLineLayer *layer in self.layerArray) {
  312. [lineArray addObject:layer.elementArray];
  313. }
  314. return @{kLFSplashViewData:@{
  315. kLFSplashViewData_layerArray:[lineArray copy],
  316. kLFSplashViewData_frameArray:[self.frameArray copy]
  317. }};
  318. }
  319. return nil;
  320. }
  321. - (void)setData:(NSDictionary *)data {
  322. NSDictionary *dataDict = data[kLFSplashViewData];
  323. NSArray *lineArray = dataDict[kLFSplashViewData_layerArray];
  324. for (NSArray *subLineArray in lineArray) {
  325. SLMosaicLineLayer *layer = [SLMosaicLineLayer layer];
  326. layer.frame = self.bounds;
  327. [layer.elementArray addObjectsFromArray:subLineArray];
  328. [self.layer addSublayer:layer];
  329. [self.layerArray addObject:layer];
  330. [layer setNeedsDisplay];
  331. }
  332. NSArray *frameArray = dataDict[kLFSplashViewData_frameArray];
  333. [self.frameArray addObjectsFromArray:frameArray];
  334. }
  335. @end