SignatureView.m 35 KB

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