2020-06-05 17:52:43 +03:00

326 lines
9.4 KiB
Objective-C

#import "TGPaintRender.h"
#include <OpenGLES/ES2/gl.h>
#import "TGPaintBrush.h"
#import "TGPaintPath.h"
#import <LegacyComponents/TGPaintUtils.h>
const NSInteger TGPaintRenderStateDefaultSize = 256;
@interface TGPaintRenderState ()
{
NSUInteger _allocatedCount;
}
@property (nonatomic, assign) CGFloat brushWeight;
@property (nonatomic, assign) CGFloat brushDynamic;
@property (nonatomic, assign) CGFloat spacing;
@property (nonatomic, assign) CGFloat alpha;
@property (nonatomic, assign) CGFloat angle;
@property (nonatomic, assign) CGFloat scale;
@property (nonatomic, readonly) CGFloat *values;
@property (nonatomic, readonly) NSUInteger count;
@property (nonatomic, assign) CGFloat remainder;
@property (nonatomic, assign) CGFloat pressureRemainder;
- (void)reset;
@end
@implementation TGPaintRenderState
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_values = NULL;
}
return self;
}
- (void)dealloc
{
if (_values != NULL)
{
free(_values);
_values = NULL;
}
}
- (void)prepare
{
if (_values != NULL)
{
free(_values);
_values = NULL;
}
_count = 0;
_allocatedCount = TGPaintRenderStateDefaultSize;
_values = malloc(sizeof(CGFloat) * TGPaintRenderStateDefaultSize * 5);
}
- (void)appendValuesCount:(NSUInteger)count
{
NSUInteger newTotalCount = _count + count;
if (newTotalCount > _allocatedCount || _values == NULL)
{
if (_values != NULL)
{
free(_values);
_values = NULL;
}
NSInteger newCount = MAX(_allocatedCount * 2, TGPaintRenderStateDefaultSize);
_values = malloc(sizeof(CGFloat) * newCount * 5);
_allocatedCount = newCount;
}
_count = newTotalCount;
}
- (void)addPoint:(CGPoint)point size:(CGFloat)size angle:(CGFloat)angle alpha:(CGFloat)alpha index:(NSInteger)index
{
NSInteger column = index * 5;
_values[column] = point.x;
_values[column + 1] = point.y;
_values[column + 2] = size;
_values[column + 3] = angle;
_values[column + 4] = alpha;
}
- (void)reset
{
_count = 0;
_allocatedCount = 0;
if (_values != NULL)
{
free(_values);
_values = NULL;
}
_remainder = 0;
_pressureRemainder = 0;
}
@end
@implementation TGPaintRender
typedef struct
{
GLfloat x;
GLfloat y;
GLfloat s;
GLfloat t;
GLfloat a;
} vertexData;
+ (void)_paintStamp:(TGPaintPoint *)point state:(TGPaintRenderState *)state
{
CGFloat brushSize = state.brushWeight * state.scale;
CGFloat angleOffset = fabs(state.angle) > FLT_EPSILON ? state.angle : 0.0f;
CGFloat alpha = MIN(1.0f, state.alpha * 1.55f);
[state prepare];
[state appendValuesCount:4];
for (NSInteger i = 0; i < 4; i++) {
[state addPoint:point.CGPoint size:brushSize angle:angleOffset alpha:alpha index:i];
}
}
+ (void)_paintFromPoint:(TGPaintPoint *)lastLocation toPoint:(TGPaintPoint *)location state:(TGPaintRenderState *)state
{
CGFloat lastP = lastLocation.z;
CGFloat p = location.z;
CGFloat pDelta = p - lastP;
CGFloat pChange = 0.0f;
CGFloat f, distance = TGPaintDistance(lastLocation.CGPoint, location.CGPoint);
CGPoint vector = TGPaintSubtractPoints(location.CGPoint, lastLocation.CGPoint);
CGPoint unitVector = CGPointMake(1.0f, 1.0f);
CGFloat vectorAngle = fabs(state.angle) > FLT_EPSILON ? state.angle : atan2(vector.y, vector.x);
CGFloat brushWeight = state.brushWeight * state.scale;
CGFloat step = MAX(1.0f, state.spacing * brushWeight);
CGFloat pressure = lastP + state.pressureRemainder;
CGFloat pressureStep = pressureStep = pDelta / ((distance - state.remainder) / step);
if (distance > 0.0f)
unitVector = TGPaintMultiplyPoint(vector, 1.0f / distance);
CGPoint start = TGPaintAddPoints(lastLocation.CGPoint, TGPaintMultiplyPoint(unitVector, state.remainder));
NSInteger i = state.count;
NSInteger count = (NSInteger)(ceil((distance - state.remainder) / step));
CGFloat boldenedAlpha = MIN(1.0f, state.alpha * 1.15f);
bool boldenFirst = lastLocation.edge;
bool boldenLast = location.edge;
[state appendValuesCount:count];
for (f = state.remainder; f <= distance; f += step, pressure += pressureStep)
{
CGFloat alpha = boldenFirst ? boldenedAlpha : state.alpha;
CGFloat brushSize = MAX(1.0, brushWeight - state.brushDynamic * pressure * brushWeight);
// CGFloat brushSize = brushWeight;
[state addPoint:start size:brushSize angle:vectorAngle alpha:alpha index:i];
start = TGPaintAddPoints(start, TGPaintMultiplyPoint(unitVector, step));
boldenFirst = false;
pChange += pressureStep;
i++;
}
// NSLog(@"final pressure %f", pressure);
if (boldenLast)
{
[state appendValuesCount:1];
CGFloat brushSize = MAX(1.0, brushWeight - state.brushDynamic * pressure * brushWeight);
[state addPoint:location.CGPoint size:brushSize angle:vectorAngle alpha:boldenedAlpha index:i];
}
state.remainder = f - distance;
state.pressureRemainder = pChange - pDelta;
}
+ (CGRect)_drawWithState:(TGPaintRenderState *)state
{
vertexData *vertexD = calloc(sizeof(vertexData), state.count * 4 + (state.count - 1) * 2);
CGRect dataBounds = CGRectZero;
int n = 0;
for (NSUInteger i = 0; i < state.count; i++)
{
NSInteger column = i * 5;
CGPoint result = CGPointMake(state.values[column], state.values[column + 1]);
CGFloat size = state.values[column + 2] / 2;
CGFloat angle = state.values[column + 3];
CGFloat alpha = state.values[column + 4];
CGRect rect = CGRectMake(result.x - size, result.y - size, size*2, size*2);
CGPoint a = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
CGPoint b = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));
CGPoint c = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));
CGPoint d = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
CGPoint center = TGPaintCenterOfRect(rect);
CGAffineTransform t = CGAffineTransformMakeTranslation(center.x, center.y);
t = CGAffineTransformRotate(t, angle);
t = CGAffineTransformTranslate(t, -center.x, -center.y);
a = CGPointApplyAffineTransform(a, t);
b = CGPointApplyAffineTransform(b, t);
c = CGPointApplyAffineTransform(c, t);
d = CGPointApplyAffineTransform(d, t);
CGRect boxBounds = CGRectApplyAffineTransform(rect, t);
dataBounds = TGPaintUnionRect(dataBounds, CGRectIntegral(boxBounds));
if (n != 0)
{
vertexD[n].x = (GLfloat)a.x;
vertexD[n].y = (GLfloat)a.y;
vertexD[n].s = (GLfloat)0;
vertexD[n].t = (GLfloat)0;
vertexD[n].a = (GLfloat)alpha;
n++;
}
vertexD[n].x = (GLfloat)a.x;
vertexD[n].y = (GLfloat)a.y;
vertexD[n].s = (GLfloat)0;
vertexD[n].t = (GLfloat)0;
vertexD[n].a = (GLfloat)alpha;
n++;
vertexD[n].x = (GLfloat)b.x;
vertexD[n].y = (GLfloat)b.y;
vertexD[n].s = (GLfloat)1;
vertexD[n].t = (GLfloat)0;
vertexD[n].a = (GLfloat)alpha;
n++;
vertexD[n].x = (GLfloat)c.x;
vertexD[n].y = (GLfloat)c.y;
vertexD[n].s = (GLfloat)0;
vertexD[n].t = (GLfloat)1;
vertexD[n].a = (GLfloat)alpha;
n++;
vertexD[n].x = (GLfloat)d.x;
vertexD[n].y = (GLfloat)d.y;
vertexD[n].s = (GLfloat)1;
vertexD[n].t = (GLfloat)1;
vertexD[n].a = (GLfloat)alpha;
n++;
if (i != (state.count - 1))
{
vertexD[n].x = (GLfloat)d.x;
vertexD[n].y = (GLfloat)d.y;
vertexD[n].s = (GLfloat)1;
vertexD[n].t = (GLfloat)1;
vertexD[n].a = (GLfloat)alpha;
n++;
}
}
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(vertexData), &vertexD[0].x);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_TRUE, sizeof(vertexData), &vertexD[0].s);
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 1, GL_FLOAT, GL_TRUE, sizeof(vertexData), &vertexD[0].a);
glEnableVertexAttribArray(2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, n);
free(vertexD);
TGPaintHasGLError();
return dataBounds;
}
+ (CGRect)renderPath:(TGPaintPath *)path renderState:(TGPaintRenderState *)renderState
{
renderState.brushWeight = path.baseWeight;
renderState.brushDynamic = path.brush.dynamic;
renderState.spacing = path.brush.spacing;
renderState.alpha = path.brush.alpha;
renderState.angle = path.brush.angle;
renderState.scale = path.brush.scale;
if (path.points.count == 1)
{
[self _paintStamp:path.points.lastObject state:renderState];
}
else
{
NSArray *points = path.points;
[renderState prepare];
for (NSUInteger i = 0; i < points.count - 1; i++)
{
[self _paintFromPoint:points[i] toPoint:points[i + 1] state:renderState];
}
}
path.remainder = renderState.remainder;
path.pressureRemainder = renderState.pressureRemainder;
return [self _drawWithState:renderState];
}
@end