| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089 |
- //
- // SignatureView.m
- // SignatureView
- //
- // Created by Michal Konturek on 05/05/2014.
- // Copyright (c) 2014 Michal Konturek. All rights reserved.
- //
- #import "SignatureView.h"
- #import <OpenGLES/ES2/glext.h>
- //
- //@interface SignatureView ()
- //
- //@property (nonatomic, strong) NSMutableArray *drawnPoints;
- //@property (nonatomic, assign) CGPoint previousPoint;
- //@property (nonatomic, strong) UIImage *tempImage;
- //
- //@property (nonatomic, assign) BOOL blank;
- //
- //@end
- //
- //@implementation SignatureView
- //
- //- (id)initWithCoder:(NSCoder *)aDecoder {
- // self = [super initWithCoder:aDecoder];
- // if (self) {
- // [self _initialize];
- // }
- // return self;
- //}
- //
- //- (id)initWithFrame:(CGRect)frame {
- // self = [super initWithFrame:frame];
- // if (self) {
- // [self _initialize];
- // }
- // return self;
- //}
- //
- //- (void)_initialize {
- //
- // self.boundary = CGRectNull;
- // self.userInteractionEnabled = YES;
- // self.blank = YES;
- //
- // [self _setupDefaultValues];
- // [self _initializeRecognizer];
- //}
- //
- //- (void)_setupDefaultValues {
- // self.foregroundLineColor = [UIColor redColor];
- // self.foregroundLineWidth = 3.0;
- //
- // self.backgroundLineColor = [UIColor blackColor];
- // self.backgroundLineWidth = 3.0;
- //}
- //
- //- (void)_initializeRecognizer {
- // id recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self
- // action:@selector(clear)];
- // self.recognizer = recognizer;
- // [self addGestureRecognizer:recognizer];
- //}
- //
- //- (void)setLineColor:(UIColor *)color {
- // self.foregroundLineColor = color;
- // self.backgroundLineColor = color;
- //}
- //
- //
- //- (void)clear {
- // self.blank = YES;
- // self.boundary = CGRectNull;
- //
- // // [self clearWithColor:[UIColor whiteColor]];
- // [self clearWithColor:[UIColor clearColor]];
- //}
- //
- //- (void)clearWithColor:(UIColor *)color {
- // CGSize screenSize = self.frame.size;
- //
- // UIGraphicsBeginImageContext(screenSize);
- //
- // CGContextRef context = UIGraphicsGetCurrentContext();
- // [self.image drawInRect:CGRectMake(0, 0, screenSize.width, screenSize.height)];
- //
- // CGContextSetFillColorWithColor(context, color.CGColor);
- // // CGContextFillRect(context, CGRectMake(0, 0, screenSize.width, screenSize.height));
- // if(color==[UIColor clearColor])
- // CGContextClearRect(context, CGRectMake(0, 0, screenSize.width, screenSize.height));
- // else
- // CGContextFillRect(context, CGRectMake(0, 0, screenSize.width, screenSize.height));
- //
- // UIImage *cleanImage = UIGraphicsGetImageFromCurrentImageContext();
- // self.image = cleanImage;
- //
- // UIGraphicsEndImageContext();
- //
- //
- //}
- //
- //- (BOOL)isSigned {
- // return !self.blank;
- //}
- //
- //- (UIImage *)signatureImage {
- //
- //
- // CGRect rect = self.boundary;
- //// rect.size.width = rect.size.width/2;
- //// rect.size.height = rect.size.height/2;
- ////
- ////
- //// cropRect = CGRectMake ((imageHeight - imageWidth) / 2.0, 0.0, imageWidth, imageWidth);
- //// and
- ////
- //// // Create new cropped UIImage
- //// UIImage * croppedImage = [UIImage imageWithCGImage: imageRef scale: chosenImage.scale orientation: chosenImage.imageOrientation];
- ////
- //
- //
- // rect.origin.x=rect.origin.x*self.image.scale;
- // rect.origin.y=rect.origin.y*self.image.scale;
- // rect.size.width=rect.size.width*self.image.scale;
- // rect.size.height=rect.size.height*self.image.scale;
- //
- //// CGImageRef imageRef = self.image.CGImage;
- //// CGImageRef imagePartRef = CGImageCreateWithImageInRect(imageRef, rect);
- //// UIImage *cropImage = [UIImage imageWithCGImage:imagePartRef scale:self.image.scale orientation:self.image.imageOrientation];
- //// CGImageRelease(imagePartRef);
- //// return cropImage;
- ////
- //// UIImage* img= [[UIImage imageWithCGImage: CGImageCreateWithImageInRect(self.image.CGImage, rect)] copy];
- //
- // CGImageRef cgimage = CGImageCreateWithImageInRect(self.image.CGImage, CGRectInset(rect, -10, -10));
- //
- // UIImage* ret=[[UIImage imageWithCGImage: cgimage] copy];
- //
- // CGImageRelease(cgimage);
- // return ret;
- // //return [self.image copy];
- //}
- //
- //- (NSData *)signatureData {
- //
- // CGImageRef cgimage =CGImageCreateWithImageInRect(self.image.CGImage, CGRectInset(self.boundary, -1, -1));
- // UIImage* img = [UIImage imageWithCGImage: cgimage];
- // CGImageRelease(cgimage);
- // return UIImagePNGRepresentation(img);
- //}
- //
- //
- //#pragma mark - Touch handlers
- //
- //- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
- //
- // CGPoint currentPoint = [self _touchPointForTouches:touches];
- // self.drawnPoints = [NSMutableArray arrayWithObject:[NSValue valueWithCGPoint:currentPoint]];
- // self.previousPoint = currentPoint;
- //
- // /*
- // To be able to replace the jagged polylines with the smooth
- // polylines, we need to save the unmodified image.
- // */
- // self.tempImage = self.image;
- //}
- //
- //- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
- //
- // CGPoint currentPoint = [self _touchPointForTouches:touches];
- // [self.drawnPoints addObject:[NSValue valueWithCGPoint:currentPoint]];
- //
- // self.image = [self _drawLineFromPoint:self.previousPoint toPoint:currentPoint image:self.image];
- // self.previousPoint = currentPoint;
- //
- // self.blank = NO;
- //}
- //
- //- (CGPoint)_touchPointForTouches:(NSSet *)touches {
- // UITouch *touch = [touches anyObject];
- // CGPoint point = [touch locationInView:self];
- // return point;
- //}
- //
- //- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
- //
- // NSArray *generalizedPoints = [self _douglasPeucker:self.drawnPoints epsilon:2];
- // NSArray *splinePoints = /*self.drawnPoints;//*/[self _catmullRomSpline:generalizedPoints segments:8];
- //
- // self.image = [self _drawLineWithPoints:splinePoints image:self.tempImage];
- //
- // // CGPoint origin = CGPointMake(MAXFLOAT, MAXFLOAT);
- // // CGRect boundary = CGRectNull;//CGRectMake(MAXFLOAT, MAXFLOAT, -MAXFLOAT, -MAXFLOAT);
- // for(int i = 0;i<splinePoints.count;i++)
- // {
- // CGPoint p = [splinePoints[i] CGPointValue];
- // CGRect rt = CGRectMake(p.x, p.y, 0, 0);
- //
- // self.boundary = CGRectUnion(rt, self.boundary);
- //
- //// if(boundary.origin.x>p.x)
- //// boundary.origin.x = p.x;
- //// if(boundary.origin.y>p.y)
- //// boundary.origin.y = p.y;
- ////
- ////
- //// if(boundary.size.height<p.y-boundary.origin.y)
- //// boundary.size.height = p.y-boundary.origin.y;
- //// if(boundary.size.width<p.x-boundary.origin.x)
- //// boundary.size.width = p.x-boundary.origin.x;
- //
- //
- //
- // }
- //
- //// CGContextRef ctx = UIGraphicsGetCurrentContext();
- ////
- //// [ [UIColor greenColor] set ];
- //// CGContextFillRect( ctx, boundary );
- // // self.debug
- //// self.debugRect = boundary;
- //// if(CGRectEqualToRect(self.debugRect, CGRectNull))
- //// self.debugRect = boundary;
- //// else
- //// self.debugRect= CGRectUnion(self.debugRect, boundary);
- //
- // self.drawnPoints = nil;
- // self.tempImage = nil;
- //}
- //
- //- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
- //
- //}
- //
- //
- //#pragma mark - Drawing
- //
- //- (UIImage *)_drawLineFromPoint:(CGPoint)fromPoint
- // toPoint:(CGPoint)toPoint image:(UIImage *)image {
- //
- // CGSize screenSize = self.frame.size;
- // if (UIGraphicsBeginImageContextWithOptions != nil) {
- // UIGraphicsBeginImageContextWithOptions(screenSize, NO, 0.0);
- // } else {
- // UIGraphicsBeginImageContext(screenSize);
- // }
- // CGContextRef context = UIGraphicsGetCurrentContext();
- //
- // [image drawInRect:CGRectMake(0, 0, screenSize.width, screenSize.height)];
- //
- // CGContextSetLineCap(context, kCGLineCapRound);
- // CGContextSetLineWidth(context, self.foregroundLineWidth);
- // CGContextSetStrokeColorWithColor(context, self.foregroundLineColor.CGColor);
- //
- // CGContextBeginPath(context);
- //
- // CGContextMoveToPoint(context, fromPoint.x, fromPoint.y);
- // CGContextAddLineToPoint(context, toPoint.x, toPoint.y);
- // CGContextStrokePath(context);
- //
- // UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
- // UIGraphicsEndImageContext();
- //
- // return result;
- //}
- //
- //- (UIImage *)_drawLineWithPoints:(NSArray *)points image:(UIImage *)image {
- //
- // CGSize screenSize = self.frame.size;
- //
- // if (UIGraphicsBeginImageContextWithOptions != nil) {
- // UIGraphicsBeginImageContextWithOptions(screenSize, NO, 0.0);
- // } else {
- // UIGraphicsBeginImageContext(screenSize);
- // }
- // CGContextRef context = UIGraphicsGetCurrentContext();
- //
- // [image drawInRect:CGRectMake(0, 0, screenSize.width, screenSize.height)];
- //
- // CGContextSetLineCap(context, kCGLineCapRound);
- // CGContextSetLineWidth(context, self.backgroundLineWidth);
- // CGContextSetStrokeColorWithColor(context, self.backgroundLineColor.CGColor);
- //
- // CGContextBeginPath(context);
- //
- // NSInteger count = [points count];
- // CGPoint point = [[points objectAtIndex:0] CGPointValue];
- // CGContextMoveToPoint(context, point.x, point.y);
- // for(int i = 1; i < count; i++) {
- // point = [[points objectAtIndex:i] CGPointValue];
- // CGContextAddLineToPoint(context, point.x, point.y);
- // }
- // CGContextStrokePath(context);
- //
- // UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
- // UIGraphicsEndImageContext();
- //
- // return result;
- //}
- //
- //
- //#pragma mark - Smoothing Alrgorithms
- //
- //- (NSArray *)_douglasPeucker:(NSArray *)points epsilon:(float)epsilon {
- //
- // NSInteger count = [points count];
- // if(count < 3) return points;
- //
- // //Find the point with the maximum distance
- // float dmax = 0;
- // int index = 0;
- // for(int i = 1; i < count - 1; i++) {
- // CGPoint point = [[points objectAtIndex:i] CGPointValue];
- // CGPoint lineA = [[points objectAtIndex:0] CGPointValue];
- // CGPoint lineB = [[points objectAtIndex:count - 1] CGPointValue];
- // float d = [self _perpendicularDistance:point lineA:lineA lineB:lineB];
- // if(d > dmax) {
- // index = i;
- // dmax = d;
- // }
- // }
- //
- // // If max distance is greater than epsilon, recursively simplify
- // NSArray *resultList;
- // if(dmax > epsilon) {
- // NSArray *recResults1 = [self _douglasPeucker:[points subarrayWithRange:NSMakeRange(0, index + 1)]
- // epsilon:epsilon];
- //
- // NSArray *recResults2 = [self _douglasPeucker:[points subarrayWithRange:NSMakeRange(index, count - index)]
- // epsilon:epsilon];
- //
- // NSMutableArray *tmpList = [NSMutableArray arrayWithArray:recResults1];
- // [tmpList removeLastObject];
- // [tmpList addObjectsFromArray:recResults2];
- // resultList = tmpList;
- // } else {
- // resultList = [NSArray arrayWithObjects:[points objectAtIndex:0], [points objectAtIndex:count - 1],nil];
- // }
- //
- // return resultList;
- //}
- //
- //- (float)_perpendicularDistance:(CGPoint)point
- // lineA:(CGPoint)lineA lineB:(CGPoint)lineB {
- //
- // CGPoint v1 = CGPointMake(lineB.x - lineA.x, lineB.y - lineA.y);
- // CGPoint v2 = CGPointMake(point.x - lineA.x, point.y - lineA.y);
- //
- // float lenV1 = sqrt(v1.x * v1.x + v1.y * v1.y);
- // float lenV2 = sqrt(v2.x * v2.x + v2.y * v2.y);
- // float angle = acos((v1.x * v2.x + v1.y * v2.y) / (lenV1 * lenV2));
- //
- // return sin(angle) * lenV2;
- //}
- //
- //- (NSArray *)_catmullRomSpline:(NSArray *)points segments:(int)segments {
- //
- // NSInteger count = [points count];
- // if(count < 4) return points;
- //
- // float b[segments][4];
- // {
- // // precompute interpolation parameters
- // float t = 0.0f;
- // float dt = 1.0f/(float)segments;
- // for (int i = 0; i < segments; i++, t+=dt) {
- // float tt = t*t;
- // float ttt = tt * t;
- // b[i][0] = 0.5f * (-ttt + 2.0f*tt - t);
- // b[i][1] = 0.5f * (3.0f*ttt -5.0f*tt +2.0f);
- // b[i][2] = 0.5f * (-3.0f*ttt + 4.0f*tt + t);
- // b[i][3] = 0.5f * (ttt - tt);
- // }
- // }
- //
- // NSMutableArray *resultArray = [NSMutableArray array];
- //
- // {
- // int i = 0; // first control point
- // [resultArray addObject:[points objectAtIndex:0]];
- // for (int j = 1; j < segments; j++) {
- // CGPoint pointI = [[points objectAtIndex:i] CGPointValue];
- // CGPoint pointIp1 = [[points objectAtIndex:(i + 1)] CGPointValue];
- // CGPoint pointIp2 = [[points objectAtIndex:(i + 2)] CGPointValue];
- // float px = (b[j][0]+b[j][1])*pointI.x + b[j][2]*pointIp1.x + b[j][3]*pointIp2.x;
- // float py = (b[j][0]+b[j][1])*pointI.y + b[j][2]*pointIp1.y + b[j][3]*pointIp2.y;
- // [resultArray addObject:[NSValue valueWithCGPoint:CGPointMake(px, py)]];
- // }
- // }
- //
- // for (int i = 1; i < count - 2; i++) {
- // // the first interpolated point is always the original control point
- // [resultArray addObject:[points objectAtIndex:i]];
- // for (int j = 1; j < segments; j++) {
- // CGPoint pointIm1 = [[points objectAtIndex:(i - 1)] CGPointValue];
- // CGPoint pointI = [[points objectAtIndex:i] CGPointValue];
- // CGPoint pointIp1 = [[points objectAtIndex:(i + 1)] CGPointValue];
- // CGPoint pointIp2 = [[points objectAtIndex:(i + 2)] CGPointValue];
- // float px = b[j][0]*pointIm1.x + b[j][1]*pointI.x + b[j][2]*pointIp1.x + b[j][3]*pointIp2.x;
- // float py = b[j][0]*pointIm1.y + b[j][1]*pointI.y + b[j][2]*pointIp1.y + b[j][3]*pointIp2.y;
- // [resultArray addObject:[NSValue valueWithCGPoint:CGPointMake(px, py)]];
- // }
- // }
- //
- // {
- // NSInteger i = count - 2; // second to last control point
- // [resultArray addObject:[points objectAtIndex:i]];
- // for (int j = 1; j < segments; j++) {
- // CGPoint pointIm1 = [[points objectAtIndex:(i - 1)] CGPointValue];
- // CGPoint pointI = [[points objectAtIndex:i] CGPointValue];
- // CGPoint pointIp1 = [[points objectAtIndex:(i + 1)] CGPointValue];
- // float px = b[j][0]*pointIm1.x + b[j][1]*pointI.x + (b[j][2]+b[j][3])*pointIp1.x;
- // float py = b[j][0]*pointIm1.y + b[j][1]*pointI.y + (b[j][2]+b[j][3])*pointIp1.y;
- // [resultArray addObject:[NSValue valueWithCGPoint:CGPointMake(px, py)]];
- // }
- // }
- //
- // // the very last interpolated point is the last control point
- // [resultArray addObject:[points objectAtIndex:(count - 1)]];
- //
- // return resultArray;
- //}
- //
- //@end
- //#define STROKE_WIDTH_MIN 0.004 // Stroke width determined by touch velocity
- //#define STROKE_WIDTH_MAX 0.010
- #define STROKE_WIDTH_SMOOTHING 0.5 // Low pass filter alpha
- #define VELOCITY_CLAMP_MIN 20
- #define VELOCITY_CLAMP_MAX 5000
- #define QUADRATIC_DISTANCE_TOLERANCE 3.0 // Minimum distance to make a curve
- #define MAXIMUM_VERTECES 100000
- static GLKVector3 StrokeColor = { 0, 0, 0 };
- static float clearColor[4] = { 1, 1, 1, 0 };
- // Vertex structure containing 3D point and color
- struct PPSSignaturePoint
- {
- GLKVector3 vertex;
- GLKVector3 color;
- };
- typedef struct PPSSignaturePoint PPSSignaturePoint;
- // Maximum verteces in signature
- static const int maxLength = MAXIMUM_VERTECES;
- // Append vertex to array buffer
- static inline void addVertex(uint *length, PPSSignaturePoint v) {
- if ((*length) >= maxLength) {
- return;
- }
-
- GLvoid *data = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
- memcpy(data + sizeof(PPSSignaturePoint) * (*length), &v, sizeof(PPSSignaturePoint));
- glUnmapBufferOES(GL_ARRAY_BUFFER);
-
- (*length)++;
- }
- static inline CGPoint QuadraticPointInCurve(CGPoint start, CGPoint end, CGPoint controlPoint, float percent) {
- double a = pow((1.0 - percent), 2.0);
- double b = 2.0 * percent * (1.0 - percent);
- double c = pow(percent, 2.0);
-
- return (CGPoint) {
- a * start.x + b * controlPoint.x + c * end.x,
- a * start.y + b * controlPoint.y + c * end.y
- };
- }
- static float generateRandom(float from, float to) { return random() % 10000 / 10000.0 * (to - from) + from; }
- static float clamp(float min, float max, float value) { return fmaxf(min, fminf(max, value)); }
- // Find perpendicular vector from two other vectors to compute triangle strip around line
- static GLKVector3 perpendicular(PPSSignaturePoint p1, PPSSignaturePoint p2) {
- GLKVector3 ret;
- ret.x = p2.vertex.y - p1.vertex.y;
- ret.y = -1 * (p2.vertex.x - p1.vertex.x);
- ret.z = 0;
- return ret;
- }
- static PPSSignaturePoint ViewPointToGL(CGPoint viewPoint, CGRect bounds, GLKVector3 color) {
-
- return (PPSSignaturePoint) {
- {
- (viewPoint.x / bounds.size.width * 2.0 - 1),
- ((viewPoint.y / bounds.size.height) * 2.0 - 1) * -1,
- 0
- },
- color
- };
- }
- @interface SignatureView ()
- @property(nonatomic,assign)CGPoint mixPoint;
- @property(nonatomic,assign)CGPoint maxPoint;
- @end
- @interface SignatureView () {
- // OpenGL state
- EAGLContext *context;
- GLKBaseEffect *effect;
-
- GLuint vertexArray;
- GLuint vertexBuffer;
- GLuint dotsArray;
- GLuint dotsBuffer;
-
-
- // Array of verteces, with current length
- PPSSignaturePoint SignatureVertexData[maxLength];
- uint length;
-
- PPSSignaturePoint SignatureDotsData[maxLength];
- uint dotsLength;
-
-
- // Width of line at current and previous vertex
- float penThickness;
- float previousThickness;
-
-
- // Previous points for quadratic bezier computations
- CGPoint previousPoint;
- CGPoint previousMidPoint;
- PPSSignaturePoint previousVertex;
- PPSSignaturePoint currentVelocity;
- }
- @end
- @implementation SignatureView
- - (void)commonInit {
-
- //#define STROKE_WIDTH_MIN 0.004 // Stroke width determined by touch velocity
- //#define STROKE_WIDTH_MAX 0.010
- self.MaxLineWidth = 0.010;
- self.MinLineWidth = 0.004;
- context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
-
- if (context) {
- time(NULL);
-
- self.backgroundColor = [UIColor clearColor];
- self.opaque = YES;
-
- self.context = context;
- self.drawableDepthFormat = GLKViewDrawableDepthFormat24;
- self.enableSetNeedsDisplay = YES;
-
- // Turn on antialiasing
- self.drawableMultisample = GLKViewDrawableMultisample4X;
-
- [self setupGL];
-
- // Capture touches
- UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
- pan.maximumNumberOfTouches = pan.minimumNumberOfTouches = 1;
- pan.cancelsTouchesInView = YES;
- [self addGestureRecognizer:pan];
-
- // For dotting your i's
- UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
- tap.cancelsTouchesInView = YES;
- tap.numberOfTapsRequired = 1;
- [self addGestureRecognizer:tap];
-
- // Erase with long press
- // UILongPressGestureRecognizer *longer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
- // longer.cancelsTouchesInView = YES;
- // [self addGestureRecognizer:longer];
-
- } else [NSException raise:@"NSOpenGLES2ContextException" format:@"Failed to create OpenGL ES2 context"];
- }
- - (id)initWithCoder:(NSCoder *)aDecoder
- {
- if (self = [super initWithCoder:aDecoder]) [self commonInit];
- return self;
- }
- - (id)initWithFrame:(CGRect)frame context:(EAGLContext *)ctx
- {
- if (self = [super initWithFrame:frame context:ctx])
- {
- [self commonInit];
-
- // self.backgroundColor =[UIColor clearColor];
-
- _mixPoint =CGPointMake(self.bounds.size.width, self.bounds.size.height);
- _maxPoint =CGPointMake(0, 0);
- // 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:@"确定"];
- // button.backgroundColor =[UIColor colorWithWhite:0.5 alpha:0.5];
- // [self addSubview:button];
- // 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:@"清除"];
- // cleanBtn.backgroundColor =[UIColor colorWithWhite:0.5 alpha:0.5];
- // [self addSubview:cleanBtn];
- }
- return self;
- }
- //- (void)okButtonClick {
- //
- // if ([self Signature2Image]) {
- //// if (_okClick) {
- ////
- //// _okClick();
- //// }
- // }
- //
- //}
- - (void)dealloc
- {
- [self tearDownGL];
-
- if ([EAGLContext currentContext] == context) {
- [EAGLContext setCurrentContext:nil];
- }
- context = nil;
- }
- - (void)drawRect:(CGRect)rect
- {
- glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
- glClear(GL_COLOR_BUFFER_BIT);
-
- [effect prepareToDraw];
-
- // Drawing of signature lines
- if (length > 2) {
- glBindVertexArrayOES(vertexArray);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, length);
- }
-
- if (dotsLength > 0) {
- glBindVertexArrayOES(dotsArray);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, dotsLength);
- }
- }
- //清除画布
- - (void)clear {
- length = 0;
- dotsLength = 0;
- self.isSigned = NO;
- _mixPoint =CGPointMake(self.bounds.size.width, self.bounds.size.height);
- _maxPoint =CGPointMake(0, 0);
- [self setNeedsDisplay];
- }
- - (void)updateRectWithPoint:(CGPoint)p
- {
- if (p.x<_mixPoint.x) {
- _mixPoint.x =p.x;
- }
- if (p.y<_mixPoint.y) {
- _mixPoint.y =p.y;
- }
- if (p.x>_maxPoint.x) {
- _maxPoint.x =p.x;
- }
- if (p.y>_maxPoint.y) {
- _maxPoint.y =p.y;
- }
- }
- //- (BOOL)saveImage
- - (UIImage*)Signature2Image
- {
- if (!self.isSigned)
- return nil;
-
- UIImage *screenshot = [self snapshot];
- if(screenshot==nil)
- {
- NSAssert(false, @"screenshot is null");
- }
- float x = _mixPoint.x - MAX_LINEWIDTH /2.0;
- float y = _mixPoint.y - MAX_LINEWIDTH / 2.0;
- if(x<0)
- x=0;
- if(y<0)
- y=0;
-
- 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);
-
- CGImageRef imageRef=screenshot.CGImage;
- CGImageRef imagePartRef=CGImageCreateWithImageInRect(imageRef,rect);
- UIImage*cropImage=[UIImage imageWithCGImage:imagePartRef];
- CGImageRelease(imagePartRef);;
- if(cropImage==nil)
- {
- NSAssert(false, @"Signature2Image: cropImage is nil");
- // int debug = 1;
- }
-
- return cropImage;
- // NSString *path =NSTemporaryDirectory();
- // NSString *url = [path stringByAppendingString:@"image.png"];
- // BOOL w = [UIImagePNGRepresentation(cropImage) writeToFile:url atomically:YES];
- // NSLog(@"%@",path);
- // [self scaleImage:cropImage toScale:1.0/screenshot.scale];
- //
- // _mixPoint =CGPointMake(self.bounds.size.width, self.bounds.size.height);
- // _maxPoint =CGPointMake(0, 0);
- // // self.strokeColor = nil;
- // //
- // // self.hidden = NO;
- // return w;
- }
- - (UIImage *)scaleImage:(UIImage *)image toScale:(float)scaleSize
- {
- UIGraphicsBeginImageContext(CGSizeMake(image.size.width * scaleSize, image.size.height * scaleSize));
- [image drawInRect:CGRectMake(0, 0, image.size.width * scaleSize+1, image.size.height * scaleSize+1)];
- UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
- NSString *path =NSTemporaryDirectory();
- NSString *url = [path stringByAppendingString:@"signImage.png"];
- [UIImagePNGRepresentation(scaledImage) writeToFile:url atomically:YES];
-
- return scaledImage;
-
- }
- #pragma mark - Gesture Recognizers
- - (void)tap:(UITapGestureRecognizer *)t {
- NSLog(@"on tap");
- CGPoint l = [t locationInView:self];
- [self updateRectWithPoint:l];
- if (t.state == UIGestureRecognizerStateRecognized) {
-
- self.isSigned = YES;
- NSLog(@"UIGestureRecognizerStateRecognized");
- glBindBuffer(GL_ARRAY_BUFFER, dotsBuffer);
-
- PPSSignaturePoint touchPoint = ViewPointToGL(l, self.bounds, (GLKVector3){1, 1, 1});
- addVertex(&dotsLength, touchPoint);
-
- PPSSignaturePoint centerPoint = touchPoint;
- centerPoint.color = StrokeColor;
- addVertex(&dotsLength, centerPoint);
-
- static int segments = 20;
- GLKVector2 radius = (GLKVector2){
- clamp(0.00001, 0.02, penThickness * generateRandom(0.5, 1.5)),
- clamp(0.00001, 0.02, penThickness * generateRandom(0.5, 1.5))
- };
- GLKVector2 velocityRadius = radius;
- float angle = 0;
-
- for (int i = 0; i <= segments; i++) {
-
- PPSSignaturePoint p = centerPoint;
- p.vertex.x += velocityRadius.x * cosf(angle);
- p.vertex.y += velocityRadius.y * sinf(angle);
-
- addVertex(&dotsLength, p);
- addVertex(&dotsLength, centerPoint);
-
- angle += M_PI * 2.0 / segments;
- }
-
- addVertex(&dotsLength, touchPoint);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- }
-
- [self setNeedsDisplay];
- }
- //- (void)longPress:(UILongPressGestureRecognizer *)lp {
- // //注释regturn,可以打开长按清除
- // return;
- //
- //// [self signatureImage];
- // [self clear];
- //}
- - (void)pan:(UIPanGestureRecognizer *)p {
-
- glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
-
- CGPoint v = [p velocityInView:self];
- CGPoint l = [p locationInView:self];
- [self updateRectWithPoint:l];
-
- currentVelocity = ViewPointToGL(v, self.bounds, (GLKVector3){0,0,0});
- float distance = 0.;
- if (previousPoint.x > 0) {
- distance = sqrtf((l.x - previousPoint.x) * (l.x - previousPoint.x) + (l.y - previousPoint.y) * (l.y - previousPoint.y));
- }
-
- float velocityMagnitude = sqrtf(v.x*v.x + v.y*v.y);
- float clampedVelocityMagnitude = clamp(VELOCITY_CLAMP_MIN, VELOCITY_CLAMP_MAX, velocityMagnitude);
- float normalizedVelocity = (clampedVelocityMagnitude - VELOCITY_CLAMP_MIN) / (VELOCITY_CLAMP_MAX - VELOCITY_CLAMP_MIN);
-
- float lowPassFilterAlpha = STROKE_WIDTH_SMOOTHING;
- float newThickness = (self.MaxLineWidth - self.MinLineWidth) * (1 - normalizedVelocity) + self.MinLineWidth;
- penThickness = penThickness * lowPassFilterAlpha + newThickness * (1 - lowPassFilterAlpha);
-
- if ([p state] == UIGestureRecognizerStateBegan) {
-
- previousPoint = l;
- previousMidPoint = l;
-
- PPSSignaturePoint startPoint = ViewPointToGL(l, self.bounds, (GLKVector3){1, 1, 1});
- previousVertex = startPoint;
- previousThickness = penThickness;
-
- addVertex(&length, startPoint);
- addVertex(&length, previousVertex);
-
- self.isSigned = YES;
-
- } else if ([p state] == UIGestureRecognizerStateChanged) {
-
- CGPoint mid = CGPointMake((l.x + previousPoint.x) / 2.0, (l.y + previousPoint.y) / 2.0);
-
- if (distance > QUADRATIC_DISTANCE_TOLERANCE) {
- // Plot quadratic bezier instead of line
- unsigned int i;
-
- int segments = (int) distance / 1.5;
-
- float startPenThickness = previousThickness;
- float endPenThickness = penThickness;
- previousThickness = penThickness;
-
- for (i = 0; i < segments; i++)
- {
- penThickness = startPenThickness + ((endPenThickness - startPenThickness) / segments) * i;
-
- CGPoint quadPoint = QuadraticPointInCurve(previousMidPoint, mid, previousPoint, (float)i / (float)(segments));
-
- PPSSignaturePoint v = ViewPointToGL(quadPoint, self.bounds, StrokeColor);
- [self addTriangleStripPointsForPrevious:previousVertex next:v];
-
- previousVertex = v;
- }
- } else if (distance > 1.0) {
-
- PPSSignaturePoint v = ViewPointToGL(l, self.bounds, StrokeColor);
- [self addTriangleStripPointsForPrevious:previousVertex next:v];
-
- previousVertex = v;
- previousThickness = penThickness;
- }
-
- previousPoint = l;
- previousMidPoint = mid;
-
- } else if (p.state == UIGestureRecognizerStateEnded | p.state == UIGestureRecognizerStateCancelled) {
-
- PPSSignaturePoint v = ViewPointToGL(l, self.bounds, (GLKVector3){1, 1, 1});
- addVertex(&length, v);
-
- previousVertex = v;
- addVertex(&length, previousVertex);
- }
-
- [self setNeedsDisplay];
- }
- - (void)setStrokeColor:(UIColor *)strokeColor {
- _strokeColor = strokeColor;
- [self updateStrokeColor];
- }
- #pragma mark - Private
- - (void)updateStrokeColor {
- CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0, white = 0.0;
- if (effect && self.strokeColor && [self.strokeColor getRed:&red green:&green blue:&blue alpha:&alpha]) {
- effect.constantColor = GLKVector4Make(red, green, blue, alpha);
- } else if (effect && self.strokeColor && [self.strokeColor getWhite:&white alpha:&alpha]) {
- effect.constantColor = GLKVector4Make(white, white, white, alpha);
- } else effect.constantColor = GLKVector4Make(0,0,0,1);
- }
- - (void)setBackgroundColor:(UIColor *)backgroundColor {
- [super setBackgroundColor:backgroundColor];
-
- CGFloat red, green, blue, alpha, white;
- if ([backgroundColor getRed:&red green:&green blue:&blue alpha:&alpha]) {
- clearColor[0] = red;
- clearColor[1] = green;
- clearColor[2] = blue;
- } else if ([backgroundColor getWhite:&white alpha:&alpha]) {
- clearColor[0] = white;
- clearColor[1] = white;
- clearColor[2] = white;
- }
- }
- - (void)bindShaderAttributes {
- glEnableVertexAttribArray(GLKVertexAttribPosition);
- glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(PPSSignaturePoint), 0);
- // glEnableVertexAttribArray(GLKVertexAttribColor);
- // glVertexAttribPointer(GLKVertexAttribColor, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)12);
- }
- - (void)setupGL
- {
- [EAGLContext setCurrentContext:context];
-
- effect = [[GLKBaseEffect alloc] init];
-
- [self updateStrokeColor];
-
-
- glDisable(GL_DEPTH_TEST);
-
- // Signature Lines
- glGenVertexArraysOES(1, &vertexArray);
- glBindVertexArrayOES(vertexArray);
-
- glGenBuffers(1, &vertexBuffer);
- glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof(SignatureVertexData), SignatureVertexData, GL_DYNAMIC_DRAW);
- [self bindShaderAttributes];
-
-
- // Signature Dots
- glGenVertexArraysOES(1, &dotsArray);
- glBindVertexArrayOES(dotsArray);
-
- glGenBuffers(1, &dotsBuffer);
- glBindBuffer(GL_ARRAY_BUFFER, dotsBuffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof(SignatureDotsData), SignatureDotsData, GL_DYNAMIC_DRAW);
- [self bindShaderAttributes];
-
-
- glBindVertexArrayOES(0);
-
-
- // Perspective
- GLKMatrix4 ortho = GLKMatrix4MakeOrtho(-1, 1, -1, 1, 0.1f, 2.0f);
- effect.transform.projectionMatrix = ortho;
-
- GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -1.0f);
- effect.transform.modelviewMatrix = modelViewMatrix;
-
- length = 0;
- penThickness = 0.003;
- previousPoint = CGPointMake(-100, -100);
- }
- - (void)addTriangleStripPointsForPrevious:(PPSSignaturePoint)previous next:(PPSSignaturePoint)next {
- float toTravel = penThickness / 2.0;
-
- for (int i = 0; i < 2; i++) {
- GLKVector3 p = perpendicular(previous, next);
- GLKVector3 p1 = next.vertex;
- GLKVector3 ref = GLKVector3Add(p1, p);
-
- float distance = GLKVector3Distance(p1, ref);
- float difX = p1.x - ref.x;
- float difY = p1.y - ref.y;
- float ratio = -1.0 * (toTravel / distance);
-
- difX = difX * ratio;
- difY = difY * ratio;
-
- PPSSignaturePoint stripPoint = {
- { p1.x + difX, p1.y + difY, 0.0 },
- StrokeColor
- };
- addVertex(&length, stripPoint);
-
- toTravel *= -1;
- }
- }
- - (void)tearDownGL
- {
- [EAGLContext setCurrentContext:context];
-
- glDeleteVertexArraysOES(1, &vertexArray);
- glDeleteBuffers(1, &vertexBuffer);
-
- glDeleteVertexArraysOES(1, &dotsArray);
- glDeleteBuffers(1, &dotsBuffer);
-
- effect = nil;
- }
- - (UIImage *)signatureImage {
- return [self Signature2Image];
- //
- // CGRect rect = self.boundary;
- //// rect.size.width = rect.size.width/2;
- //// rect.size.height = rect.size.height/2;
- ////
- ////
- //// cropRect = CGRectMake ((imageHeight - imageWidth) / 2.0, 0.0, imageWidth, imageWidth);
- //// and
- ////
- //// // Create new cropped UIImage
- //// UIImage * croppedImage = [UIImage imageWithCGImage: imageRef scale: chosenImage.scale orientation: chosenImage.imageOrientation];
- ////
- //
- //
- // rect.origin.x=rect.origin.x*self.image.scale;
- // rect.origin.y=rect.origin.y*self.image.scale;
- // rect.size.width=rect.size.width*self.image.scale;
- // rect.size.height=rect.size.height*self.image.scale;
- //
- //// CGImageRef imageRef = self.image.CGImage;
- //// CGImageRef imagePartRef = CGImageCreateWithImageInRect(imageRef, rect);
- //// UIImage *cropImage = [UIImage imageWithCGImage:imagePartRef scale:self.image.scale orientation:self.image.imageOrientation];
- //// CGImageRelease(imagePartRef);
- //// return cropImage;
- ////
- //// UIImage* img= [[UIImage imageWithCGImage: CGImageCreateWithImageInRect(self.image.CGImage, rect)] copy];
- //
- // CGImageRef cgimage = CGImageCreateWithImageInRect(self.image.CGImage, CGRectInset(rect, -10, -10));
- //
- // UIImage* ret=[[UIImage imageWithCGImage: cgimage] copy];
- //
- // CGImageRelease(cgimage);
- // return ret;
- //return [self.image copy];
- }
- - (NSData *)signatureData {
- UIImage* signatureImage = [self Signature2Image];
- // CGImageRef cgimage =CGImageCreateWithImageInRect(signatureImage.CGImage, CGRectInset(self.boundary, -1, -1));
- // UIImage* img = [UIImage imageWithCGImage: cgimage];
- // CGImageRelease(cgimage);
- return UIImagePNGRepresentation(signatureImage);
- }
- - (void)setLineWidth:(CGFloat)width {
- self.MaxLineWidth = width/400.0;
- self.MinLineWidth = width/400.0*0.4;
- // self.foregroundLineWidth = width;
-
-
- // currentVelocity = ViewPointToGL(v, self.bounds, (GLKVector3){0,0,0});
- // float distance = 0.;
- // if (previousPoint.x > 0) {
- // distance = sqrtf((l.x - previousPoint.x) * (l.x - previousPoint.x) + (l.y - previousPoint.y) * (l.y - previousPoint.y));
- // }
- //
- // float velocityMagnitude = sqrtf(v.x*v.x + v.y*v.y);
- // float clampedVelocityMagnitude = clamp(VELOCITY_CLAMP_MIN, VELOCITY_CLAMP_MAX, velocityMagnitude);
- // float normalizedVelocity = (clampedVelocityMagnitude - VELOCITY_CLAMP_MIN) / (VELOCITY_CLAMP_MAX - VELOCITY_CLAMP_MIN);
- //
-
- penThickness = (self.MaxLineWidth - self.MinLineWidth)/2.0;
- // float lowPassFilterAlpha = STROKE_WIDTH_SMOOTHING;
- // float newThickness = (self.MaxLineWidth - self.MinLineWidth) * (1 - normalizedVelocity) + self.MinLineWidth;
- // penThickness = penThickness * lowPassFilterAlpha + newThickness * (1 - lowPassFilterAlpha);
- }
- #pragma mark - Touch handlers
- - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
-
- }
- @end
|