SignatureView.m 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. //
  2. // SignatureView.m
  3. // SignatureView
  4. //
  5. // Created by Michal Konturek on 05/05/2014.
  6. // Copyright (c) 2014 Michal Konturek. All rights reserved.
  7. //
  8. #import "SignatureView.h"
  9. #import <OpenGLES/ES2/glext.h>
  10. //
  11. //@interface SignatureView ()
  12. //
  13. //@property (nonatomic, strong) NSMutableArray *drawnPoints;
  14. //@property (nonatomic, assign) CGPoint previousPoint;
  15. //@property (nonatomic, strong) UIImage *tempImage;
  16. //
  17. //@property (nonatomic, assign) BOOL blank;
  18. //
  19. //@end
  20. //
  21. //@implementation SignatureView
  22. //
  23. //- (id)initWithCoder:(NSCoder *)aDecoder {
  24. // self = [super initWithCoder:aDecoder];
  25. // if (self) {
  26. // [self _initialize];
  27. // }
  28. // return self;
  29. //}
  30. //
  31. //- (id)initWithFrame:(CGRect)frame {
  32. // self = [super initWithFrame:frame];
  33. // if (self) {
  34. // [self _initialize];
  35. // }
  36. // return self;
  37. //}
  38. //
  39. //- (void)_initialize {
  40. //
  41. // self.boundary = CGRectNull;
  42. // self.userInteractionEnabled = YES;
  43. // self.blank = YES;
  44. //
  45. // [self _setupDefaultValues];
  46. // [self _initializeRecognizer];
  47. //}
  48. //
  49. //- (void)_setupDefaultValues {
  50. // self.foregroundLineColor = [UIColor redColor];
  51. // self.foregroundLineWidth = 3.0;
  52. //
  53. // self.backgroundLineColor = [UIColor blackColor];
  54. // self.backgroundLineWidth = 3.0;
  55. //}
  56. //
  57. //- (void)_initializeRecognizer {
  58. // id recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self
  59. // action:@selector(clear)];
  60. // self.recognizer = recognizer;
  61. // [self addGestureRecognizer:recognizer];
  62. //}
  63. //
  64. //- (void)setLineColor:(UIColor *)color {
  65. // self.foregroundLineColor = color;
  66. // self.backgroundLineColor = color;
  67. //}
  68. //
  69. //
  70. //- (void)clear {
  71. // self.blank = YES;
  72. // self.boundary = CGRectNull;
  73. //
  74. // // [self clearWithColor:[UIColor whiteColor]];
  75. // [self clearWithColor:[UIColor clearColor]];
  76. //}
  77. //
  78. //- (void)clearWithColor:(UIColor *)color {
  79. // CGSize screenSize = self.frame.size;
  80. //
  81. // UIGraphicsBeginImageContext(screenSize);
  82. //
  83. // CGContextRef context = UIGraphicsGetCurrentContext();
  84. // [self.image drawInRect:CGRectMake(0, 0, screenSize.width, screenSize.height)];
  85. //
  86. // CGContextSetFillColorWithColor(context, color.CGColor);
  87. // // CGContextFillRect(context, CGRectMake(0, 0, screenSize.width, screenSize.height));
  88. // if(color==[UIColor clearColor])
  89. // CGContextClearRect(context, CGRectMake(0, 0, screenSize.width, screenSize.height));
  90. // else
  91. // CGContextFillRect(context, CGRectMake(0, 0, screenSize.width, screenSize.height));
  92. //
  93. // UIImage *cleanImage = UIGraphicsGetImageFromCurrentImageContext();
  94. // self.image = cleanImage;
  95. //
  96. // UIGraphicsEndImageContext();
  97. //
  98. //
  99. //}
  100. //
  101. //- (BOOL)isSigned {
  102. // return !self.blank;
  103. //}
  104. //
  105. //- (UIImage *)signatureImage {
  106. //
  107. //
  108. // CGRect rect = self.boundary;
  109. //// rect.size.width = rect.size.width/2;
  110. //// rect.size.height = rect.size.height/2;
  111. ////
  112. ////
  113. //// cropRect = CGRectMake ((imageHeight - imageWidth) / 2.0, 0.0, imageWidth, imageWidth);
  114. //// and
  115. ////
  116. //// // Create new cropped UIImage
  117. //// UIImage * croppedImage = [UIImage imageWithCGImage: imageRef scale: chosenImage.scale orientation: chosenImage.imageOrientation];
  118. ////
  119. //
  120. //
  121. // rect.origin.x=rect.origin.x*self.image.scale;
  122. // rect.origin.y=rect.origin.y*self.image.scale;
  123. // rect.size.width=rect.size.width*self.image.scale;
  124. // rect.size.height=rect.size.height*self.image.scale;
  125. //
  126. //// CGImageRef imageRef = self.image.CGImage;
  127. //// CGImageRef imagePartRef = CGImageCreateWithImageInRect(imageRef, rect);
  128. //// UIImage *cropImage = [UIImage imageWithCGImage:imagePartRef scale:self.image.scale orientation:self.image.imageOrientation];
  129. //// CGImageRelease(imagePartRef);
  130. //// return cropImage;
  131. ////
  132. //// UIImage* img= [[UIImage imageWithCGImage: CGImageCreateWithImageInRect(self.image.CGImage, rect)] copy];
  133. //
  134. // CGImageRef cgimage = CGImageCreateWithImageInRect(self.image.CGImage, CGRectInset(rect, -10, -10));
  135. //
  136. // UIImage* ret=[[UIImage imageWithCGImage: cgimage] copy];
  137. //
  138. // CGImageRelease(cgimage);
  139. // return ret;
  140. // //return [self.image copy];
  141. //}
  142. //
  143. //- (NSData *)signatureData {
  144. //
  145. // CGImageRef cgimage =CGImageCreateWithImageInRect(self.image.CGImage, CGRectInset(self.boundary, -1, -1));
  146. // UIImage* img = [UIImage imageWithCGImage: cgimage];
  147. // CGImageRelease(cgimage);
  148. // return UIImagePNGRepresentation(img);
  149. //}
  150. //
  151. //
  152. //#pragma mark - Touch handlers
  153. //
  154. //- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  155. //
  156. // CGPoint currentPoint = [self _touchPointForTouches:touches];
  157. // self.drawnPoints = [NSMutableArray arrayWithObject:[NSValue valueWithCGPoint:currentPoint]];
  158. // self.previousPoint = currentPoint;
  159. //
  160. // /*
  161. // To be able to replace the jagged polylines with the smooth
  162. // polylines, we need to save the unmodified image.
  163. // */
  164. // self.tempImage = self.image;
  165. //}
  166. //
  167. //- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  168. //
  169. // CGPoint currentPoint = [self _touchPointForTouches:touches];
  170. // [self.drawnPoints addObject:[NSValue valueWithCGPoint:currentPoint]];
  171. //
  172. // self.image = [self _drawLineFromPoint:self.previousPoint toPoint:currentPoint image:self.image];
  173. // self.previousPoint = currentPoint;
  174. //
  175. // self.blank = NO;
  176. //}
  177. //
  178. //- (CGPoint)_touchPointForTouches:(NSSet *)touches {
  179. // UITouch *touch = [touches anyObject];
  180. // CGPoint point = [touch locationInView:self];
  181. // return point;
  182. //}
  183. //
  184. //- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  185. //
  186. // NSArray *generalizedPoints = [self _douglasPeucker:self.drawnPoints epsilon:2];
  187. // NSArray *splinePoints = /*self.drawnPoints;//*/[self _catmullRomSpline:generalizedPoints segments:8];
  188. //
  189. // self.image = [self _drawLineWithPoints:splinePoints image:self.tempImage];
  190. //
  191. // // CGPoint origin = CGPointMake(MAXFLOAT, MAXFLOAT);
  192. // // CGRect boundary = CGRectNull;//CGRectMake(MAXFLOAT, MAXFLOAT, -MAXFLOAT, -MAXFLOAT);
  193. // for(int i = 0;i<splinePoints.count;i++)
  194. // {
  195. // CGPoint p = [splinePoints[i] CGPointValue];
  196. // CGRect rt = CGRectMake(p.x, p.y, 0, 0);
  197. //
  198. // self.boundary = CGRectUnion(rt, self.boundary);
  199. //
  200. //// if(boundary.origin.x>p.x)
  201. //// boundary.origin.x = p.x;
  202. //// if(boundary.origin.y>p.y)
  203. //// boundary.origin.y = p.y;
  204. ////
  205. ////
  206. //// if(boundary.size.height<p.y-boundary.origin.y)
  207. //// boundary.size.height = p.y-boundary.origin.y;
  208. //// if(boundary.size.width<p.x-boundary.origin.x)
  209. //// boundary.size.width = p.x-boundary.origin.x;
  210. //
  211. //
  212. //
  213. // }
  214. //
  215. //// CGContextRef ctx = UIGraphicsGetCurrentContext();
  216. ////
  217. //// [ [UIColor greenColor] set ];
  218. //// CGContextFillRect( ctx, boundary );
  219. // // self.debug
  220. //// self.debugRect = boundary;
  221. //// if(CGRectEqualToRect(self.debugRect, CGRectNull))
  222. //// self.debugRect = boundary;
  223. //// else
  224. //// self.debugRect= CGRectUnion(self.debugRect, boundary);
  225. //
  226. // self.drawnPoints = nil;
  227. // self.tempImage = nil;
  228. //}
  229. //
  230. //- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
  231. //
  232. //}
  233. //
  234. //
  235. //#pragma mark - Drawing
  236. //
  237. //- (UIImage *)_drawLineFromPoint:(CGPoint)fromPoint
  238. // toPoint:(CGPoint)toPoint image:(UIImage *)image {
  239. //
  240. // CGSize screenSize = self.frame.size;
  241. // if (UIGraphicsBeginImageContextWithOptions != nil) {
  242. // UIGraphicsBeginImageContextWithOptions(screenSize, NO, 0.0);
  243. // } else {
  244. // UIGraphicsBeginImageContext(screenSize);
  245. // }
  246. // CGContextRef context = UIGraphicsGetCurrentContext();
  247. //
  248. // [image drawInRect:CGRectMake(0, 0, screenSize.width, screenSize.height)];
  249. //
  250. // CGContextSetLineCap(context, kCGLineCapRound);
  251. // CGContextSetLineWidth(context, self.foregroundLineWidth);
  252. // CGContextSetStrokeColorWithColor(context, self.foregroundLineColor.CGColor);
  253. //
  254. // CGContextBeginPath(context);
  255. //
  256. // CGContextMoveToPoint(context, fromPoint.x, fromPoint.y);
  257. // CGContextAddLineToPoint(context, toPoint.x, toPoint.y);
  258. // CGContextStrokePath(context);
  259. //
  260. // UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
  261. // UIGraphicsEndImageContext();
  262. //
  263. // return result;
  264. //}
  265. //
  266. //- (UIImage *)_drawLineWithPoints:(NSArray *)points image:(UIImage *)image {
  267. //
  268. // CGSize screenSize = self.frame.size;
  269. //
  270. // if (UIGraphicsBeginImageContextWithOptions != nil) {
  271. // UIGraphicsBeginImageContextWithOptions(screenSize, NO, 0.0);
  272. // } else {
  273. // UIGraphicsBeginImageContext(screenSize);
  274. // }
  275. // CGContextRef context = UIGraphicsGetCurrentContext();
  276. //
  277. // [image drawInRect:CGRectMake(0, 0, screenSize.width, screenSize.height)];
  278. //
  279. // CGContextSetLineCap(context, kCGLineCapRound);
  280. // CGContextSetLineWidth(context, self.backgroundLineWidth);
  281. // CGContextSetStrokeColorWithColor(context, self.backgroundLineColor.CGColor);
  282. //
  283. // CGContextBeginPath(context);
  284. //
  285. // NSInteger count = [points count];
  286. // CGPoint point = [[points objectAtIndex:0] CGPointValue];
  287. // CGContextMoveToPoint(context, point.x, point.y);
  288. // for(int i = 1; i < count; i++) {
  289. // point = [[points objectAtIndex:i] CGPointValue];
  290. // CGContextAddLineToPoint(context, point.x, point.y);
  291. // }
  292. // CGContextStrokePath(context);
  293. //
  294. // UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
  295. // UIGraphicsEndImageContext();
  296. //
  297. // return result;
  298. //}
  299. //
  300. //
  301. //#pragma mark - Smoothing Alrgorithms
  302. //
  303. //- (NSArray *)_douglasPeucker:(NSArray *)points epsilon:(float)epsilon {
  304. //
  305. // NSInteger count = [points count];
  306. // if(count < 3) return points;
  307. //
  308. // //Find the point with the maximum distance
  309. // float dmax = 0;
  310. // int index = 0;
  311. // for(int i = 1; i < count - 1; i++) {
  312. // CGPoint point = [[points objectAtIndex:i] CGPointValue];
  313. // CGPoint lineA = [[points objectAtIndex:0] CGPointValue];
  314. // CGPoint lineB = [[points objectAtIndex:count - 1] CGPointValue];
  315. // float d = [self _perpendicularDistance:point lineA:lineA lineB:lineB];
  316. // if(d > dmax) {
  317. // index = i;
  318. // dmax = d;
  319. // }
  320. // }
  321. //
  322. // // If max distance is greater than epsilon, recursively simplify
  323. // NSArray *resultList;
  324. // if(dmax > epsilon) {
  325. // NSArray *recResults1 = [self _douglasPeucker:[points subarrayWithRange:NSMakeRange(0, index + 1)]
  326. // epsilon:epsilon];
  327. //
  328. // NSArray *recResults2 = [self _douglasPeucker:[points subarrayWithRange:NSMakeRange(index, count - index)]
  329. // epsilon:epsilon];
  330. //
  331. // NSMutableArray *tmpList = [NSMutableArray arrayWithArray:recResults1];
  332. // [tmpList removeLastObject];
  333. // [tmpList addObjectsFromArray:recResults2];
  334. // resultList = tmpList;
  335. // } else {
  336. // resultList = [NSArray arrayWithObjects:[points objectAtIndex:0], [points objectAtIndex:count - 1],nil];
  337. // }
  338. //
  339. // return resultList;
  340. //}
  341. //
  342. //- (float)_perpendicularDistance:(CGPoint)point
  343. // lineA:(CGPoint)lineA lineB:(CGPoint)lineB {
  344. //
  345. // CGPoint v1 = CGPointMake(lineB.x - lineA.x, lineB.y - lineA.y);
  346. // CGPoint v2 = CGPointMake(point.x - lineA.x, point.y - lineA.y);
  347. //
  348. // float lenV1 = sqrt(v1.x * v1.x + v1.y * v1.y);
  349. // float lenV2 = sqrt(v2.x * v2.x + v2.y * v2.y);
  350. // float angle = acos((v1.x * v2.x + v1.y * v2.y) / (lenV1 * lenV2));
  351. //
  352. // return sin(angle) * lenV2;
  353. //}
  354. //
  355. //- (NSArray *)_catmullRomSpline:(NSArray *)points segments:(int)segments {
  356. //
  357. // NSInteger count = [points count];
  358. // if(count < 4) return points;
  359. //
  360. // float b[segments][4];
  361. // {
  362. // // precompute interpolation parameters
  363. // float t = 0.0f;
  364. // float dt = 1.0f/(float)segments;
  365. // for (int i = 0; i < segments; i++, t+=dt) {
  366. // float tt = t*t;
  367. // float ttt = tt * t;
  368. // b[i][0] = 0.5f * (-ttt + 2.0f*tt - t);
  369. // b[i][1] = 0.5f * (3.0f*ttt -5.0f*tt +2.0f);
  370. // b[i][2] = 0.5f * (-3.0f*ttt + 4.0f*tt + t);
  371. // b[i][3] = 0.5f * (ttt - tt);
  372. // }
  373. // }
  374. //
  375. // NSMutableArray *resultArray = [NSMutableArray array];
  376. //
  377. // {
  378. // int i = 0; // first control point
  379. // [resultArray addObject:[points objectAtIndex:0]];
  380. // for (int j = 1; j < segments; j++) {
  381. // CGPoint pointI = [[points objectAtIndex:i] CGPointValue];
  382. // CGPoint pointIp1 = [[points objectAtIndex:(i + 1)] CGPointValue];
  383. // CGPoint pointIp2 = [[points objectAtIndex:(i + 2)] CGPointValue];
  384. // float px = (b[j][0]+b[j][1])*pointI.x + b[j][2]*pointIp1.x + b[j][3]*pointIp2.x;
  385. // float py = (b[j][0]+b[j][1])*pointI.y + b[j][2]*pointIp1.y + b[j][3]*pointIp2.y;
  386. // [resultArray addObject:[NSValue valueWithCGPoint:CGPointMake(px, py)]];
  387. // }
  388. // }
  389. //
  390. // for (int i = 1; i < count - 2; i++) {
  391. // // the first interpolated point is always the original control point
  392. // [resultArray addObject:[points objectAtIndex:i]];
  393. // for (int j = 1; j < segments; j++) {
  394. // CGPoint pointIm1 = [[points objectAtIndex:(i - 1)] CGPointValue];
  395. // CGPoint pointI = [[points objectAtIndex:i] CGPointValue];
  396. // CGPoint pointIp1 = [[points objectAtIndex:(i + 1)] CGPointValue];
  397. // CGPoint pointIp2 = [[points objectAtIndex:(i + 2)] CGPointValue];
  398. // float px = b[j][0]*pointIm1.x + b[j][1]*pointI.x + b[j][2]*pointIp1.x + b[j][3]*pointIp2.x;
  399. // float py = b[j][0]*pointIm1.y + b[j][1]*pointI.y + b[j][2]*pointIp1.y + b[j][3]*pointIp2.y;
  400. // [resultArray addObject:[NSValue valueWithCGPoint:CGPointMake(px, py)]];
  401. // }
  402. // }
  403. //
  404. // {
  405. // NSInteger i = count - 2; // second to last control point
  406. // [resultArray addObject:[points objectAtIndex:i]];
  407. // for (int j = 1; j < segments; j++) {
  408. // CGPoint pointIm1 = [[points objectAtIndex:(i - 1)] CGPointValue];
  409. // CGPoint pointI = [[points objectAtIndex:i] CGPointValue];
  410. // CGPoint pointIp1 = [[points objectAtIndex:(i + 1)] CGPointValue];
  411. // float px = b[j][0]*pointIm1.x + b[j][1]*pointI.x + (b[j][2]+b[j][3])*pointIp1.x;
  412. // float py = b[j][0]*pointIm1.y + b[j][1]*pointI.y + (b[j][2]+b[j][3])*pointIp1.y;
  413. // [resultArray addObject:[NSValue valueWithCGPoint:CGPointMake(px, py)]];
  414. // }
  415. // }
  416. //
  417. // // the very last interpolated point is the last control point
  418. // [resultArray addObject:[points objectAtIndex:(count - 1)]];
  419. //
  420. // return resultArray;
  421. //}
  422. //
  423. //@end
  424. //#define STROKE_WIDTH_MIN 0.004 // Stroke width determined by touch velocity
  425. //#define STROKE_WIDTH_MAX 0.010
  426. #define STROKE_WIDTH_SMOOTHING 0.5 // Low pass filter alpha
  427. #define VELOCITY_CLAMP_MIN 20
  428. #define VELOCITY_CLAMP_MAX 5000
  429. #define QUADRATIC_DISTANCE_TOLERANCE 3.0 // Minimum distance to make a curve
  430. #define MAXIMUM_VERTECES 100000
  431. static GLKVector3 StrokeColor = { 0, 0, 0 };
  432. static float clearColor[4] = { 1, 1, 1, 0 };
  433. // Vertex structure containing 3D point and color
  434. struct PPSSignaturePoint
  435. {
  436. GLKVector3 vertex;
  437. GLKVector3 color;
  438. };
  439. typedef struct PPSSignaturePoint PPSSignaturePoint;
  440. // Maximum verteces in signature
  441. static const int maxLength = MAXIMUM_VERTECES;
  442. // Append vertex to array buffer
  443. static inline void addVertex(uint *length, PPSSignaturePoint v) {
  444. if ((*length) >= maxLength) {
  445. return;
  446. }
  447. GLvoid *data = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
  448. memcpy(data + sizeof(PPSSignaturePoint) * (*length), &v, sizeof(PPSSignaturePoint));
  449. glUnmapBufferOES(GL_ARRAY_BUFFER);
  450. (*length)++;
  451. }
  452. static inline CGPoint QuadraticPointInCurve(CGPoint start, CGPoint end, CGPoint controlPoint, float percent) {
  453. double a = pow((1.0 - percent), 2.0);
  454. double b = 2.0 * percent * (1.0 - percent);
  455. double c = pow(percent, 2.0);
  456. return (CGPoint) {
  457. a * start.x + b * controlPoint.x + c * end.x,
  458. a * start.y + b * controlPoint.y + c * end.y
  459. };
  460. }
  461. static float generateRandom(float from, float to) { return random() % 10000 / 10000.0 * (to - from) + from; }
  462. static float clamp(float min, float max, float value) { return fmaxf(min, fminf(max, value)); }
  463. // Find perpendicular vector from two other vectors to compute triangle strip around line
  464. static GLKVector3 perpendicular(PPSSignaturePoint p1, PPSSignaturePoint p2) {
  465. GLKVector3 ret;
  466. ret.x = p2.vertex.y - p1.vertex.y;
  467. ret.y = -1 * (p2.vertex.x - p1.vertex.x);
  468. ret.z = 0;
  469. return ret;
  470. }
  471. static PPSSignaturePoint ViewPointToGL(CGPoint viewPoint, CGRect bounds, GLKVector3 color) {
  472. return (PPSSignaturePoint) {
  473. {
  474. (viewPoint.x / bounds.size.width * 2.0 - 1),
  475. ((viewPoint.y / bounds.size.height) * 2.0 - 1) * -1,
  476. 0
  477. },
  478. color
  479. };
  480. }
  481. @interface SignatureView ()
  482. @property(nonatomic,assign)CGPoint mixPoint;
  483. @property(nonatomic,assign)CGPoint maxPoint;
  484. @end
  485. @interface SignatureView () {
  486. // OpenGL state
  487. EAGLContext *context;
  488. GLKBaseEffect *effect;
  489. GLuint vertexArray;
  490. GLuint vertexBuffer;
  491. GLuint dotsArray;
  492. GLuint dotsBuffer;
  493. // Array of verteces, with current length
  494. PPSSignaturePoint SignatureVertexData[maxLength];
  495. uint length;
  496. PPSSignaturePoint SignatureDotsData[maxLength];
  497. uint dotsLength;
  498. // Width of line at current and previous vertex
  499. float penThickness;
  500. float previousThickness;
  501. // Previous points for quadratic bezier computations
  502. CGPoint previousPoint;
  503. CGPoint previousMidPoint;
  504. PPSSignaturePoint previousVertex;
  505. PPSSignaturePoint currentVelocity;
  506. }
  507. @end
  508. @implementation SignatureView
  509. - (void)commonInit {
  510. //#define STROKE_WIDTH_MIN 0.004 // Stroke width determined by touch velocity
  511. //#define STROKE_WIDTH_MAX 0.010
  512. self.MaxLineWidth = 0.010;
  513. self.MinLineWidth = 0.004;
  514. context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
  515. if (context) {
  516. time(NULL);
  517. self.backgroundColor = [UIColor clearColor];
  518. self.opaque = YES;
  519. self.context = context;
  520. self.drawableDepthFormat = GLKViewDrawableDepthFormat24;
  521. self.enableSetNeedsDisplay = YES;
  522. // Turn on antialiasing
  523. self.drawableMultisample = GLKViewDrawableMultisample4X;
  524. [self setupGL];
  525. // Capture touches
  526. UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
  527. pan.maximumNumberOfTouches = pan.minimumNumberOfTouches = 1;
  528. pan.cancelsTouchesInView = YES;
  529. [self addGestureRecognizer:pan];
  530. // For dotting your i's
  531. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
  532. tap.cancelsTouchesInView = YES;
  533. tap.numberOfTapsRequired = 1;
  534. [self addGestureRecognizer:tap];
  535. // Erase with long press
  536. // UILongPressGestureRecognizer *longer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
  537. // longer.cancelsTouchesInView = YES;
  538. // [self addGestureRecognizer:longer];
  539. } else [NSException raise:@"NSOpenGLES2ContextException" format:@"Failed to create OpenGL ES2 context"];
  540. }
  541. - (id)initWithCoder:(NSCoder *)aDecoder
  542. {
  543. if (self = [super initWithCoder:aDecoder]) [self commonInit];
  544. return self;
  545. }
  546. - (id)initWithFrame:(CGRect)frame context:(EAGLContext *)ctx
  547. {
  548. if (self = [super initWithFrame:frame context:ctx])
  549. {
  550. [self commonInit];
  551. // self.backgroundColor =[UIColor clearColor];
  552. _mixPoint =CGPointMake(self.bounds.size.width, self.bounds.size.height);
  553. _maxPoint =CGPointMake(0, 0);
  554. // UIButton *button =[JDUtils createButtonWithFrame:CGRectMake(0,self.frame.size.height-50, self.frame.size.width/2, 50) ImageName:nil Target:self Action:@selector(okButtonClick) Title:@"确定"];
  555. // button.backgroundColor =[UIColor colorWithWhite:0.5 alpha:0.5];
  556. // [self addSubview:button];
  557. // UIButton *cleanBtn = [JDUtils createButtonWithFrame:CGRectMake(SCREEN_WIDHT/2,self.frame.size.height-50, self.frame.size.width/2, 50) ImageName:nil Target:self Action:@selector(erase) Title:@"清除"];
  558. // cleanBtn.backgroundColor =[UIColor colorWithWhite:0.5 alpha:0.5];
  559. // [self addSubview:cleanBtn];
  560. }
  561. return self;
  562. }
  563. //- (void)okButtonClick {
  564. //
  565. // if ([self Signature2Image]) {
  566. //// if (_okClick) {
  567. ////
  568. //// _okClick();
  569. //// }
  570. // }
  571. //
  572. //}
  573. - (void)dealloc
  574. {
  575. [self tearDownGL];
  576. if ([EAGLContext currentContext] == context) {
  577. [EAGLContext setCurrentContext:nil];
  578. }
  579. context = nil;
  580. }
  581. - (void)drawRect:(CGRect)rect
  582. {
  583. glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
  584. glClear(GL_COLOR_BUFFER_BIT);
  585. [effect prepareToDraw];
  586. // Drawing of signature lines
  587. if (length > 2) {
  588. glBindVertexArrayOES(vertexArray);
  589. glDrawArrays(GL_TRIANGLE_STRIP, 0, length);
  590. }
  591. if (dotsLength > 0) {
  592. glBindVertexArrayOES(dotsArray);
  593. glDrawArrays(GL_TRIANGLE_STRIP, 0, dotsLength);
  594. }
  595. }
  596. //清除画布
  597. - (void)clear {
  598. length = 0;
  599. dotsLength = 0;
  600. self.isSigned = NO;
  601. _mixPoint =CGPointMake(self.bounds.size.width, self.bounds.size.height);
  602. _maxPoint =CGPointMake(0, 0);
  603. [self setNeedsDisplay];
  604. }
  605. - (void)updateRectWithPoint:(CGPoint)p
  606. {
  607. if (p.x<_mixPoint.x) {
  608. _mixPoint.x =p.x;
  609. }
  610. if (p.y<_mixPoint.y) {
  611. _mixPoint.y =p.y;
  612. }
  613. if (p.x>_maxPoint.x) {
  614. _maxPoint.x =p.x;
  615. }
  616. if (p.y>_maxPoint.y) {
  617. _maxPoint.y =p.y;
  618. }
  619. }
  620. //- (BOOL)saveImage
  621. - (UIImage*)Signature2Image
  622. {
  623. if (!self.isSigned)
  624. return nil;
  625. UIImage *screenshot = [self snapshot];
  626. if(screenshot==nil)
  627. {
  628. NSAssert(false, @"screenshot is null");
  629. }
  630. float x = _mixPoint.x - MAX_LINEWIDTH /2.0;
  631. float y = _mixPoint.y - MAX_LINEWIDTH / 2.0;
  632. if(x<0)
  633. x=0;
  634. if(y<0)
  635. y=0;
  636. CGRect rect = CGRectMake(x*screenshot.scale, y*screenshot.scale, (_maxPoint.x-_mixPoint.x+MAX_LINEWIDTH)*screenshot.scale, (_maxPoint.y-_mixPoint.y+MAX_LINEWIDTH)*screenshot.scale);
  637. CGImageRef imageRef=screenshot.CGImage;
  638. CGImageRef imagePartRef=CGImageCreateWithImageInRect(imageRef,rect);
  639. UIImage*cropImage=[UIImage imageWithCGImage:imagePartRef];
  640. CGImageRelease(imagePartRef);;
  641. if(cropImage==nil)
  642. {
  643. NSAssert(false, @"Signature2Image: cropImage is nil");
  644. // int debug = 1;
  645. }
  646. return cropImage;
  647. // NSString *path =NSTemporaryDirectory();
  648. // NSString *url = [path stringByAppendingString:@"image.png"];
  649. // BOOL w = [UIImagePNGRepresentation(cropImage) writeToFile:url atomically:YES];
  650. // NSLog(@"%@",path);
  651. // [self scaleImage:cropImage toScale:1.0/screenshot.scale];
  652. //
  653. // _mixPoint =CGPointMake(self.bounds.size.width, self.bounds.size.height);
  654. // _maxPoint =CGPointMake(0, 0);
  655. // // self.strokeColor = nil;
  656. // //
  657. // // self.hidden = NO;
  658. // return w;
  659. }
  660. - (UIImage *)scaleImage:(UIImage *)image toScale:(float)scaleSize
  661. {
  662. UIGraphicsBeginImageContext(CGSizeMake(image.size.width * scaleSize, image.size.height * scaleSize));
  663. [image drawInRect:CGRectMake(0, 0, image.size.width * scaleSize+1, image.size.height * scaleSize+1)];
  664. UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
  665. UIGraphicsEndImageContext();
  666. NSString *path =NSTemporaryDirectory();
  667. NSString *url = [path stringByAppendingString:@"signImage.png"];
  668. [UIImagePNGRepresentation(scaledImage) writeToFile:url atomically:YES];
  669. return scaledImage;
  670. }
  671. #pragma mark - Gesture Recognizers
  672. - (void)tap:(UITapGestureRecognizer *)t {
  673. NSLog(@"on tap");
  674. CGPoint l = [t locationInView:self];
  675. [self updateRectWithPoint:l];
  676. if (t.state == UIGestureRecognizerStateRecognized) {
  677. self.isSigned = YES;
  678. NSLog(@"UIGestureRecognizerStateRecognized");
  679. glBindBuffer(GL_ARRAY_BUFFER, dotsBuffer);
  680. PPSSignaturePoint touchPoint = ViewPointToGL(l, self.bounds, (GLKVector3){1, 1, 1});
  681. addVertex(&dotsLength, touchPoint);
  682. PPSSignaturePoint centerPoint = touchPoint;
  683. centerPoint.color = StrokeColor;
  684. addVertex(&dotsLength, centerPoint);
  685. static int segments = 20;
  686. GLKVector2 radius = (GLKVector2){
  687. clamp(0.00001, 0.02, penThickness * generateRandom(0.5, 1.5)),
  688. clamp(0.00001, 0.02, penThickness * generateRandom(0.5, 1.5))
  689. };
  690. GLKVector2 velocityRadius = radius;
  691. float angle = 0;
  692. for (int i = 0; i <= segments; i++) {
  693. PPSSignaturePoint p = centerPoint;
  694. p.vertex.x += velocityRadius.x * cosf(angle);
  695. p.vertex.y += velocityRadius.y * sinf(angle);
  696. addVertex(&dotsLength, p);
  697. addVertex(&dotsLength, centerPoint);
  698. angle += M_PI * 2.0 / segments;
  699. }
  700. addVertex(&dotsLength, touchPoint);
  701. glBindBuffer(GL_ARRAY_BUFFER, 0);
  702. }
  703. [self setNeedsDisplay];
  704. }
  705. //- (void)longPress:(UILongPressGestureRecognizer *)lp {
  706. // //注释regturn,可以打开长按清除
  707. // return;
  708. //
  709. //// [self signatureImage];
  710. // [self clear];
  711. //}
  712. - (void)pan:(UIPanGestureRecognizer *)p {
  713. glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
  714. CGPoint v = [p velocityInView:self];
  715. CGPoint l = [p locationInView:self];
  716. [self updateRectWithPoint:l];
  717. currentVelocity = ViewPointToGL(v, self.bounds, (GLKVector3){0,0,0});
  718. float distance = 0.;
  719. if (previousPoint.x > 0) {
  720. distance = sqrtf((l.x - previousPoint.x) * (l.x - previousPoint.x) + (l.y - previousPoint.y) * (l.y - previousPoint.y));
  721. }
  722. float velocityMagnitude = sqrtf(v.x*v.x + v.y*v.y);
  723. float clampedVelocityMagnitude = clamp(VELOCITY_CLAMP_MIN, VELOCITY_CLAMP_MAX, velocityMagnitude);
  724. float normalizedVelocity = (clampedVelocityMagnitude - VELOCITY_CLAMP_MIN) / (VELOCITY_CLAMP_MAX - VELOCITY_CLAMP_MIN);
  725. float lowPassFilterAlpha = STROKE_WIDTH_SMOOTHING;
  726. float newThickness = (self.MaxLineWidth - self.MinLineWidth) * (1 - normalizedVelocity) + self.MinLineWidth;
  727. penThickness = penThickness * lowPassFilterAlpha + newThickness * (1 - lowPassFilterAlpha);
  728. if ([p state] == UIGestureRecognizerStateBegan) {
  729. previousPoint = l;
  730. previousMidPoint = l;
  731. PPSSignaturePoint startPoint = ViewPointToGL(l, self.bounds, (GLKVector3){1, 1, 1});
  732. previousVertex = startPoint;
  733. previousThickness = penThickness;
  734. addVertex(&length, startPoint);
  735. addVertex(&length, previousVertex);
  736. self.isSigned = YES;
  737. } else if ([p state] == UIGestureRecognizerStateChanged) {
  738. CGPoint mid = CGPointMake((l.x + previousPoint.x) / 2.0, (l.y + previousPoint.y) / 2.0);
  739. if (distance > QUADRATIC_DISTANCE_TOLERANCE) {
  740. // Plot quadratic bezier instead of line
  741. unsigned int i;
  742. int segments = (int) distance / 1.5;
  743. float startPenThickness = previousThickness;
  744. float endPenThickness = penThickness;
  745. previousThickness = penThickness;
  746. for (i = 0; i < segments; i++)
  747. {
  748. penThickness = startPenThickness + ((endPenThickness - startPenThickness) / segments) * i;
  749. CGPoint quadPoint = QuadraticPointInCurve(previousMidPoint, mid, previousPoint, (float)i / (float)(segments));
  750. PPSSignaturePoint v = ViewPointToGL(quadPoint, self.bounds, StrokeColor);
  751. [self addTriangleStripPointsForPrevious:previousVertex next:v];
  752. previousVertex = v;
  753. }
  754. } else if (distance > 1.0) {
  755. PPSSignaturePoint v = ViewPointToGL(l, self.bounds, StrokeColor);
  756. [self addTriangleStripPointsForPrevious:previousVertex next:v];
  757. previousVertex = v;
  758. previousThickness = penThickness;
  759. }
  760. previousPoint = l;
  761. previousMidPoint = mid;
  762. } else if (p.state == UIGestureRecognizerStateEnded | p.state == UIGestureRecognizerStateCancelled) {
  763. PPSSignaturePoint v = ViewPointToGL(l, self.bounds, (GLKVector3){1, 1, 1});
  764. addVertex(&length, v);
  765. previousVertex = v;
  766. addVertex(&length, previousVertex);
  767. }
  768. [self setNeedsDisplay];
  769. }
  770. - (void)setStrokeColor:(UIColor *)strokeColor {
  771. _strokeColor = strokeColor;
  772. [self updateStrokeColor];
  773. }
  774. #pragma mark - Private
  775. - (void)updateStrokeColor {
  776. CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0, white = 0.0;
  777. if (effect && self.strokeColor && [self.strokeColor getRed:&red green:&green blue:&blue alpha:&alpha]) {
  778. effect.constantColor = GLKVector4Make(red, green, blue, alpha);
  779. } else if (effect && self.strokeColor && [self.strokeColor getWhite:&white alpha:&alpha]) {
  780. effect.constantColor = GLKVector4Make(white, white, white, alpha);
  781. } else effect.constantColor = GLKVector4Make(0,0,0,1);
  782. }
  783. - (void)setBackgroundColor:(UIColor *)backgroundColor {
  784. [super setBackgroundColor:backgroundColor];
  785. CGFloat red, green, blue, alpha, white;
  786. if ([backgroundColor getRed:&red green:&green blue:&blue alpha:&alpha]) {
  787. clearColor[0] = red;
  788. clearColor[1] = green;
  789. clearColor[2] = blue;
  790. } else if ([backgroundColor getWhite:&white alpha:&alpha]) {
  791. clearColor[0] = white;
  792. clearColor[1] = white;
  793. clearColor[2] = white;
  794. }
  795. }
  796. - (void)bindShaderAttributes {
  797. glEnableVertexAttribArray(GLKVertexAttribPosition);
  798. glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(PPSSignaturePoint), 0);
  799. // glEnableVertexAttribArray(GLKVertexAttribColor);
  800. // glVertexAttribPointer(GLKVertexAttribColor, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)12);
  801. }
  802. - (void)setupGL
  803. {
  804. [EAGLContext setCurrentContext:context];
  805. effect = [[GLKBaseEffect alloc] init];
  806. [self updateStrokeColor];
  807. glDisable(GL_DEPTH_TEST);
  808. // Signature Lines
  809. glGenVertexArraysOES(1, &vertexArray);
  810. glBindVertexArrayOES(vertexArray);
  811. glGenBuffers(1, &vertexBuffer);
  812. glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
  813. glBufferData(GL_ARRAY_BUFFER, sizeof(SignatureVertexData), SignatureVertexData, GL_DYNAMIC_DRAW);
  814. [self bindShaderAttributes];
  815. // Signature Dots
  816. glGenVertexArraysOES(1, &dotsArray);
  817. glBindVertexArrayOES(dotsArray);
  818. glGenBuffers(1, &dotsBuffer);
  819. glBindBuffer(GL_ARRAY_BUFFER, dotsBuffer);
  820. glBufferData(GL_ARRAY_BUFFER, sizeof(SignatureDotsData), SignatureDotsData, GL_DYNAMIC_DRAW);
  821. [self bindShaderAttributes];
  822. glBindVertexArrayOES(0);
  823. // Perspective
  824. GLKMatrix4 ortho = GLKMatrix4MakeOrtho(-1, 1, -1, 1, 0.1f, 2.0f);
  825. effect.transform.projectionMatrix = ortho;
  826. GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -1.0f);
  827. effect.transform.modelviewMatrix = modelViewMatrix;
  828. length = 0;
  829. penThickness = 0.003;
  830. previousPoint = CGPointMake(-100, -100);
  831. }
  832. - (void)addTriangleStripPointsForPrevious:(PPSSignaturePoint)previous next:(PPSSignaturePoint)next {
  833. float toTravel = penThickness / 2.0;
  834. for (int i = 0; i < 2; i++) {
  835. GLKVector3 p = perpendicular(previous, next);
  836. GLKVector3 p1 = next.vertex;
  837. GLKVector3 ref = GLKVector3Add(p1, p);
  838. float distance = GLKVector3Distance(p1, ref);
  839. float difX = p1.x - ref.x;
  840. float difY = p1.y - ref.y;
  841. float ratio = -1.0 * (toTravel / distance);
  842. difX = difX * ratio;
  843. difY = difY * ratio;
  844. PPSSignaturePoint stripPoint = {
  845. { p1.x + difX, p1.y + difY, 0.0 },
  846. StrokeColor
  847. };
  848. addVertex(&length, stripPoint);
  849. toTravel *= -1;
  850. }
  851. }
  852. - (void)tearDownGL
  853. {
  854. [EAGLContext setCurrentContext:context];
  855. glDeleteVertexArraysOES(1, &vertexArray);
  856. glDeleteBuffers(1, &vertexBuffer);
  857. glDeleteVertexArraysOES(1, &dotsArray);
  858. glDeleteBuffers(1, &dotsBuffer);
  859. effect = nil;
  860. }
  861. - (UIImage *)signatureImage {
  862. return [self Signature2Image];
  863. //
  864. // CGRect rect = self.boundary;
  865. //// rect.size.width = rect.size.width/2;
  866. //// rect.size.height = rect.size.height/2;
  867. ////
  868. ////
  869. //// cropRect = CGRectMake ((imageHeight - imageWidth) / 2.0, 0.0, imageWidth, imageWidth);
  870. //// and
  871. ////
  872. //// // Create new cropped UIImage
  873. //// UIImage * croppedImage = [UIImage imageWithCGImage: imageRef scale: chosenImage.scale orientation: chosenImage.imageOrientation];
  874. ////
  875. //
  876. //
  877. // rect.origin.x=rect.origin.x*self.image.scale;
  878. // rect.origin.y=rect.origin.y*self.image.scale;
  879. // rect.size.width=rect.size.width*self.image.scale;
  880. // rect.size.height=rect.size.height*self.image.scale;
  881. //
  882. //// CGImageRef imageRef = self.image.CGImage;
  883. //// CGImageRef imagePartRef = CGImageCreateWithImageInRect(imageRef, rect);
  884. //// UIImage *cropImage = [UIImage imageWithCGImage:imagePartRef scale:self.image.scale orientation:self.image.imageOrientation];
  885. //// CGImageRelease(imagePartRef);
  886. //// return cropImage;
  887. ////
  888. //// UIImage* img= [[UIImage imageWithCGImage: CGImageCreateWithImageInRect(self.image.CGImage, rect)] copy];
  889. //
  890. // CGImageRef cgimage = CGImageCreateWithImageInRect(self.image.CGImage, CGRectInset(rect, -10, -10));
  891. //
  892. // UIImage* ret=[[UIImage imageWithCGImage: cgimage] copy];
  893. //
  894. // CGImageRelease(cgimage);
  895. // return ret;
  896. //return [self.image copy];
  897. }
  898. - (NSData *)signatureData {
  899. UIImage* signatureImage = [self Signature2Image];
  900. // CGImageRef cgimage =CGImageCreateWithImageInRect(signatureImage.CGImage, CGRectInset(self.boundary, -1, -1));
  901. // UIImage* img = [UIImage imageWithCGImage: cgimage];
  902. // CGImageRelease(cgimage);
  903. return UIImagePNGRepresentation(signatureImage);
  904. }
  905. - (void)setLineWidth:(CGFloat)width {
  906. self.MaxLineWidth = width/400.0;
  907. self.MinLineWidth = width/400.0*0.4;
  908. // self.foregroundLineWidth = width;
  909. // currentVelocity = ViewPointToGL(v, self.bounds, (GLKVector3){0,0,0});
  910. // float distance = 0.;
  911. // if (previousPoint.x > 0) {
  912. // distance = sqrtf((l.x - previousPoint.x) * (l.x - previousPoint.x) + (l.y - previousPoint.y) * (l.y - previousPoint.y));
  913. // }
  914. //
  915. // float velocityMagnitude = sqrtf(v.x*v.x + v.y*v.y);
  916. // float clampedVelocityMagnitude = clamp(VELOCITY_CLAMP_MIN, VELOCITY_CLAMP_MAX, velocityMagnitude);
  917. // float normalizedVelocity = (clampedVelocityMagnitude - VELOCITY_CLAMP_MIN) / (VELOCITY_CLAMP_MAX - VELOCITY_CLAMP_MIN);
  918. //
  919. penThickness = (self.MaxLineWidth - self.MinLineWidth)/2.0;
  920. // float lowPassFilterAlpha = STROKE_WIDTH_SMOOTHING;
  921. // float newThickness = (self.MaxLineWidth - self.MinLineWidth) * (1 - normalizedVelocity) + self.MinLineWidth;
  922. // penThickness = penThickness * lowPassFilterAlpha + newThickness * (1 - lowPassFilterAlpha);
  923. }
  924. #pragma mark - Touch handlers
  925. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  926. }
  927. @end