SignatureView.m 35 KB

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