This app provides monitoring and information features for the common freifunk user and the technical stuff of a freifunk community.
Code base is taken from a TUM Practical Course project and added here to see if Freifunk Altdorf can use it.
https://www.freifunk-altdorf.de
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
533 lines
20 KiB
533 lines
20 KiB
/** |
|
* Copyright (c) 2015-present, Facebook, Inc. |
|
* |
|
* This source code is licensed under the MIT license found in the |
|
* LICENSE file in the root directory of this source tree. |
|
*/ |
|
|
|
#import "RCTBorderDrawing.h" |
|
#import "RCTLog.h" |
|
|
|
static const CGFloat RCTViewBorderThreshold = 0.001; |
|
|
|
BOOL RCTBorderInsetsAreEqual(UIEdgeInsets borderInsets) |
|
{ |
|
return |
|
ABS(borderInsets.left - borderInsets.right) < RCTViewBorderThreshold && |
|
ABS(borderInsets.left - borderInsets.bottom) < RCTViewBorderThreshold && |
|
ABS(borderInsets.left - borderInsets.top) < RCTViewBorderThreshold; |
|
} |
|
|
|
BOOL RCTCornerRadiiAreEqual(RCTCornerRadii cornerRadii) |
|
{ |
|
return |
|
ABS(cornerRadii.topLeft - cornerRadii.topRight) < RCTViewBorderThreshold && |
|
ABS(cornerRadii.topLeft - cornerRadii.bottomLeft) < RCTViewBorderThreshold && |
|
ABS(cornerRadii.topLeft - cornerRadii.bottomRight) < RCTViewBorderThreshold; |
|
} |
|
|
|
BOOL RCTBorderColorsAreEqual(RCTBorderColors borderColors) |
|
{ |
|
return |
|
CGColorEqualToColor(borderColors.left, borderColors.right) && |
|
CGColorEqualToColor(borderColors.left, borderColors.top) && |
|
CGColorEqualToColor(borderColors.left, borderColors.bottom); |
|
} |
|
|
|
RCTCornerInsets RCTGetCornerInsets(RCTCornerRadii cornerRadii, |
|
UIEdgeInsets edgeInsets) |
|
{ |
|
return (RCTCornerInsets) { |
|
{ |
|
MAX(0, cornerRadii.topLeft - edgeInsets.left), |
|
MAX(0, cornerRadii.topLeft - edgeInsets.top), |
|
}, |
|
{ |
|
MAX(0, cornerRadii.topRight - edgeInsets.right), |
|
MAX(0, cornerRadii.topRight - edgeInsets.top), |
|
}, |
|
{ |
|
MAX(0, cornerRadii.bottomLeft - edgeInsets.left), |
|
MAX(0, cornerRadii.bottomLeft - edgeInsets.bottom), |
|
}, |
|
{ |
|
MAX(0, cornerRadii.bottomRight - edgeInsets.right), |
|
MAX(0, cornerRadii.bottomRight - edgeInsets.bottom), |
|
} |
|
}; |
|
} |
|
|
|
static UIEdgeInsets RCTRoundInsetsToPixel(UIEdgeInsets edgeInsets) { |
|
edgeInsets.top = RCTRoundPixelValue(edgeInsets.top); |
|
edgeInsets.bottom = RCTRoundPixelValue(edgeInsets.bottom); |
|
edgeInsets.left = RCTRoundPixelValue(edgeInsets.left); |
|
edgeInsets.right = RCTRoundPixelValue(edgeInsets.right); |
|
|
|
return edgeInsets; |
|
} |
|
|
|
static void RCTPathAddEllipticArc(CGMutablePathRef path, |
|
const CGAffineTransform *m, |
|
CGPoint origin, |
|
CGSize size, |
|
CGFloat startAngle, |
|
CGFloat endAngle, |
|
BOOL clockwise) |
|
{ |
|
CGFloat xScale = 1, yScale = 1, radius = 0; |
|
if (size.width != 0) { |
|
xScale = 1; |
|
yScale = size.height / size.width; |
|
radius = size.width; |
|
} else if (size.height != 0) { |
|
xScale = size.width / size.height; |
|
yScale = 1; |
|
radius = size.height; |
|
} |
|
|
|
CGAffineTransform t = CGAffineTransformMakeTranslation(origin.x, origin.y); |
|
t = CGAffineTransformScale(t, xScale, yScale); |
|
if (m != NULL) { |
|
t = CGAffineTransformConcat(t, *m); |
|
} |
|
|
|
CGPathAddArc(path, &t, 0, 0, radius, startAngle, endAngle, clockwise); |
|
} |
|
|
|
CGPathRef RCTPathCreateWithRoundedRect(CGRect bounds, |
|
RCTCornerInsets cornerInsets, |
|
const CGAffineTransform *transform) |
|
{ |
|
const CGFloat minX = CGRectGetMinX(bounds); |
|
const CGFloat minY = CGRectGetMinY(bounds); |
|
const CGFloat maxX = CGRectGetMaxX(bounds); |
|
const CGFloat maxY = CGRectGetMaxY(bounds); |
|
|
|
const CGSize topLeft = { |
|
MAX(0, MIN(cornerInsets.topLeft.width, bounds.size.width - cornerInsets.topRight.width)), |
|
MAX(0, MIN(cornerInsets.topLeft.height, bounds.size.height - cornerInsets.bottomLeft.height)), |
|
}; |
|
const CGSize topRight = { |
|
MAX(0, MIN(cornerInsets.topRight.width, bounds.size.width - cornerInsets.topLeft.width)), |
|
MAX(0, MIN(cornerInsets.topRight.height, bounds.size.height - cornerInsets.bottomRight.height)), |
|
}; |
|
const CGSize bottomLeft = { |
|
MAX(0, MIN(cornerInsets.bottomLeft.width, bounds.size.width - cornerInsets.bottomRight.width)), |
|
MAX(0, MIN(cornerInsets.bottomLeft.height, bounds.size.height - cornerInsets.topLeft.height)), |
|
}; |
|
const CGSize bottomRight = { |
|
MAX(0, MIN(cornerInsets.bottomRight.width, bounds.size.width - cornerInsets.bottomLeft.width)), |
|
MAX(0, MIN(cornerInsets.bottomRight.height, bounds.size.height - cornerInsets.topRight.height)), |
|
}; |
|
|
|
CGMutablePathRef path = CGPathCreateMutable(); |
|
RCTPathAddEllipticArc(path, transform, (CGPoint){ |
|
minX + topLeft.width, minY + topLeft.height |
|
}, topLeft, M_PI, 3 * M_PI_2, NO); |
|
RCTPathAddEllipticArc(path, transform, (CGPoint){ |
|
maxX - topRight.width, minY + topRight.height |
|
}, topRight, 3 * M_PI_2, 0, NO); |
|
RCTPathAddEllipticArc(path, transform, (CGPoint){ |
|
maxX - bottomRight.width, maxY - bottomRight.height |
|
}, bottomRight, 0, M_PI_2, NO); |
|
RCTPathAddEllipticArc(path, transform, (CGPoint){ |
|
minX + bottomLeft.width, maxY - bottomLeft.height |
|
}, bottomLeft, M_PI_2, M_PI, NO); |
|
CGPathCloseSubpath(path); |
|
return path; |
|
} |
|
|
|
static void RCTEllipseGetIntersectionsWithLine(CGRect ellipseBounds, |
|
CGPoint lineStart, |
|
CGPoint lineEnd, |
|
CGPoint intersections[2]) |
|
{ |
|
const CGPoint ellipseCenter = { |
|
CGRectGetMidX(ellipseBounds), |
|
CGRectGetMidY(ellipseBounds) |
|
}; |
|
|
|
lineStart.x -= ellipseCenter.x; |
|
lineStart.y -= ellipseCenter.y; |
|
lineEnd.x -= ellipseCenter.x; |
|
lineEnd.y -= ellipseCenter.y; |
|
|
|
const CGFloat m = (lineEnd.y - lineStart.y) / (lineEnd.x - lineStart.x); |
|
const CGFloat a = ellipseBounds.size.width / 2; |
|
const CGFloat b = ellipseBounds.size.height / 2; |
|
const CGFloat c = lineStart.y - m * lineStart.x; |
|
const CGFloat A = (b * b + a * a * m * m); |
|
const CGFloat B = 2 * a * a * c * m; |
|
const CGFloat D = sqrt((a * a * (b * b - c * c)) / A + pow(B / (2 * A), 2)); |
|
|
|
const CGFloat x_ = -B / (2 * A); |
|
const CGFloat x1 = x_ + D; |
|
const CGFloat x2 = x_ - D; |
|
const CGFloat y1 = m * x1 + c; |
|
const CGFloat y2 = m * x2 + c; |
|
|
|
intersections[0] = (CGPoint){x1 + ellipseCenter.x, y1 + ellipseCenter.y}; |
|
intersections[1] = (CGPoint){x2 + ellipseCenter.x, y2 + ellipseCenter.y}; |
|
} |
|
|
|
NS_INLINE BOOL RCTCornerRadiiAreAboveThreshold(RCTCornerRadii cornerRadii) { |
|
return (cornerRadii.topLeft > RCTViewBorderThreshold || |
|
cornerRadii.topRight > RCTViewBorderThreshold || |
|
cornerRadii.bottomLeft > RCTViewBorderThreshold || |
|
cornerRadii.bottomRight > RCTViewBorderThreshold); |
|
} |
|
|
|
static CGPathRef RCTPathCreateOuterOutline(BOOL drawToEdge, CGRect rect, RCTCornerRadii cornerRadii) { |
|
if (drawToEdge) { |
|
return CGPathCreateWithRect(rect, NULL); |
|
} |
|
|
|
return RCTPathCreateWithRoundedRect(rect, RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL); |
|
} |
|
|
|
static CGContextRef RCTUIGraphicsBeginImageContext(CGSize size, CGColorRef backgroundColor, BOOL hasCornerRadii, BOOL drawToEdge) { |
|
const CGFloat alpha = CGColorGetAlpha(backgroundColor); |
|
const BOOL opaque = (drawToEdge || !hasCornerRadii) && alpha == 1.0; |
|
UIGraphicsBeginImageContextWithOptions(size, opaque, 0.0); |
|
return UIGraphicsGetCurrentContext(); |
|
} |
|
|
|
static UIImage *RCTGetSolidBorderImage(RCTCornerRadii cornerRadii, |
|
CGSize viewSize, |
|
UIEdgeInsets borderInsets, |
|
RCTBorderColors borderColors, |
|
CGColorRef backgroundColor, |
|
BOOL drawToEdge) |
|
{ |
|
const BOOL hasCornerRadii = RCTCornerRadiiAreAboveThreshold(cornerRadii); |
|
const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, borderInsets); |
|
|
|
// Incorrect render for borders that are not proportional to device pixel: borders get stretched and become |
|
// significantly bigger than expected. |
|
// Rdar: http://www.openradar.me/15959788 |
|
borderInsets = RCTRoundInsetsToPixel(borderInsets); |
|
|
|
const BOOL makeStretchable = |
|
(borderInsets.left + cornerInsets.topLeft.width + |
|
borderInsets.right + cornerInsets.bottomRight.width <= viewSize.width) && |
|
(borderInsets.left + cornerInsets.bottomLeft.width + |
|
borderInsets.right + cornerInsets.topRight.width <= viewSize.width) && |
|
(borderInsets.top + cornerInsets.topLeft.height + |
|
borderInsets.bottom + cornerInsets.bottomRight.height <= viewSize.height) && |
|
(borderInsets.top + cornerInsets.topRight.height + |
|
borderInsets.bottom + cornerInsets.bottomLeft.height <= viewSize.height); |
|
|
|
const UIEdgeInsets edgeInsets = (UIEdgeInsets){ |
|
borderInsets.top + MAX(cornerInsets.topLeft.height, cornerInsets.topRight.height), |
|
borderInsets.left + MAX(cornerInsets.topLeft.width, cornerInsets.bottomLeft.width), |
|
borderInsets.bottom + MAX(cornerInsets.bottomLeft.height, cornerInsets.bottomRight.height), |
|
borderInsets.right + MAX(cornerInsets.bottomRight.width, cornerInsets.topRight.width) |
|
}; |
|
|
|
const CGSize size = makeStretchable ? (CGSize){ |
|
// 1pt for the middle stretchable area along each axis |
|
edgeInsets.left + 1 + edgeInsets.right, |
|
edgeInsets.top + 1 + edgeInsets.bottom |
|
} : viewSize; |
|
|
|
CGContextRef ctx = RCTUIGraphicsBeginImageContext(size, backgroundColor, hasCornerRadii, drawToEdge); |
|
const CGRect rect = {.size = size}; |
|
CGPathRef path = RCTPathCreateOuterOutline(drawToEdge, rect, cornerRadii); |
|
|
|
if (backgroundColor) { |
|
CGContextSetFillColorWithColor(ctx, backgroundColor); |
|
CGContextAddPath(ctx, path); |
|
CGContextFillPath(ctx); |
|
} |
|
|
|
CGContextAddPath(ctx, path); |
|
CGPathRelease(path); |
|
|
|
CGPathRef insetPath = RCTPathCreateWithRoundedRect(UIEdgeInsetsInsetRect(rect, borderInsets), cornerInsets, NULL); |
|
|
|
CGContextAddPath(ctx, insetPath); |
|
CGContextEOClip(ctx); |
|
|
|
BOOL hasEqualColors = RCTBorderColorsAreEqual(borderColors); |
|
if ((drawToEdge || !hasCornerRadii) && hasEqualColors) { |
|
|
|
CGContextSetFillColorWithColor(ctx, borderColors.left); |
|
CGContextAddRect(ctx, rect); |
|
CGContextAddPath(ctx, insetPath); |
|
CGContextEOFillPath(ctx); |
|
|
|
} else { |
|
|
|
CGPoint topLeft = (CGPoint){borderInsets.left, borderInsets.top}; |
|
if (cornerInsets.topLeft.width > 0 && cornerInsets.topLeft.height > 0) { |
|
CGPoint points[2]; |
|
RCTEllipseGetIntersectionsWithLine((CGRect){ |
|
topLeft, {2 * cornerInsets.topLeft.width, 2 * cornerInsets.topLeft.height} |
|
}, CGPointZero, topLeft, points); |
|
if (!isnan(points[1].x) && !isnan(points[1].y)) { |
|
topLeft = points[1]; |
|
} |
|
} |
|
|
|
CGPoint bottomLeft = (CGPoint){borderInsets.left, size.height - borderInsets.bottom}; |
|
if (cornerInsets.bottomLeft.width > 0 && cornerInsets.bottomLeft.height > 0) { |
|
CGPoint points[2]; |
|
RCTEllipseGetIntersectionsWithLine((CGRect){ |
|
{bottomLeft.x, bottomLeft.y - 2 * cornerInsets.bottomLeft.height}, |
|
{2 * cornerInsets.bottomLeft.width, 2 * cornerInsets.bottomLeft.height} |
|
}, (CGPoint){0, size.height}, bottomLeft, points); |
|
if (!isnan(points[1].x) && !isnan(points[1].y)) { |
|
bottomLeft = points[1]; |
|
} |
|
} |
|
|
|
CGPoint topRight = (CGPoint){size.width - borderInsets.right, borderInsets.top}; |
|
if (cornerInsets.topRight.width > 0 && cornerInsets.topRight.height > 0) { |
|
CGPoint points[2]; |
|
RCTEllipseGetIntersectionsWithLine((CGRect){ |
|
{topRight.x - 2 * cornerInsets.topRight.width, topRight.y}, |
|
{2 * cornerInsets.topRight.width, 2 * cornerInsets.topRight.height} |
|
}, (CGPoint){size.width, 0}, topRight, points); |
|
if (!isnan(points[0].x) && !isnan(points[0].y)) { |
|
topRight = points[0]; |
|
} |
|
} |
|
|
|
CGPoint bottomRight = (CGPoint){size.width - borderInsets.right, size.height - borderInsets.bottom}; |
|
if (cornerInsets.bottomRight.width > 0 && cornerInsets.bottomRight.height > 0) { |
|
CGPoint points[2]; |
|
RCTEllipseGetIntersectionsWithLine((CGRect){ |
|
{bottomRight.x - 2 * cornerInsets.bottomRight.width, bottomRight.y - 2 * cornerInsets.bottomRight.height}, |
|
{2 * cornerInsets.bottomRight.width, 2 * cornerInsets.bottomRight.height} |
|
}, (CGPoint){size.width, size.height}, bottomRight, points); |
|
if (!isnan(points[0].x) && !isnan(points[0].y)) { |
|
bottomRight = points[0]; |
|
} |
|
} |
|
|
|
CGColorRef currentColor = NULL; |
|
|
|
// RIGHT |
|
if (borderInsets.right > 0) { |
|
|
|
const CGPoint points[] = { |
|
(CGPoint){size.width, 0}, |
|
topRight, |
|
bottomRight, |
|
(CGPoint){size.width, size.height}, |
|
}; |
|
|
|
currentColor = borderColors.right; |
|
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points)); |
|
} |
|
|
|
// BOTTOM |
|
if (borderInsets.bottom > 0) { |
|
|
|
const CGPoint points[] = { |
|
(CGPoint){0, size.height}, |
|
bottomLeft, |
|
bottomRight, |
|
(CGPoint){size.width, size.height}, |
|
}; |
|
|
|
if (!CGColorEqualToColor(currentColor, borderColors.bottom)) { |
|
CGContextSetFillColorWithColor(ctx, currentColor); |
|
CGContextFillPath(ctx); |
|
currentColor = borderColors.bottom; |
|
} |
|
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points)); |
|
} |
|
|
|
// LEFT |
|
if (borderInsets.left > 0) { |
|
|
|
const CGPoint points[] = { |
|
CGPointZero, |
|
topLeft, |
|
bottomLeft, |
|
(CGPoint){0, size.height}, |
|
}; |
|
|
|
if (!CGColorEqualToColor(currentColor, borderColors.left)) { |
|
CGContextSetFillColorWithColor(ctx, currentColor); |
|
CGContextFillPath(ctx); |
|
currentColor = borderColors.left; |
|
} |
|
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points)); |
|
} |
|
|
|
// TOP |
|
if (borderInsets.top > 0) { |
|
|
|
const CGPoint points[] = { |
|
CGPointZero, |
|
topLeft, |
|
topRight, |
|
(CGPoint){size.width, 0}, |
|
}; |
|
|
|
if (!CGColorEqualToColor(currentColor, borderColors.top)) { |
|
CGContextSetFillColorWithColor(ctx, currentColor); |
|
CGContextFillPath(ctx); |
|
currentColor = borderColors.top; |
|
} |
|
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points)); |
|
} |
|
|
|
CGContextSetFillColorWithColor(ctx, currentColor); |
|
CGContextFillPath(ctx); |
|
} |
|
|
|
CGPathRelease(insetPath); |
|
|
|
UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); |
|
UIGraphicsEndImageContext(); |
|
|
|
if (makeStretchable) { |
|
image = [image resizableImageWithCapInsets:edgeInsets]; |
|
} |
|
|
|
return image; |
|
} |
|
|
|
// Currently, the dashed / dotted implementation only supports a single colour + |
|
// single width, as that's currently required and supported on Android. |
|
// |
|
// Supporting individual widths + colours on each side is possible by modifying |
|
// the current implementation. The idea is that we will draw four different lines |
|
// and clip appropriately for each side (might require adjustment of phase so that |
|
// they line up but even browsers don't do a good job at that). |
|
// |
|
// Firstly, create two paths for the outer and inner paths. The inner path is |
|
// generated exactly the same way as the outer, just given an inset rect, derived |
|
// from the insets on each side. Then clip using the odd-even rule |
|
// (CGContextEOClip()). This will give us a nice rounded (possibly) clip mask. |
|
// |
|
// +----------------------------------+ |
|
// |@@@@@@@@ Clipped Space @@@@@@@@@| |
|
// |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| |
|
// |@@+----------------------+@@@@@@@@| |
|
// |@@| |@@@@@@@@| |
|
// |@@| |@@@@@@@@| |
|
// |@@| |@@@@@@@@| |
|
// |@@+----------------------+@@@@@@@@| |
|
// |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| |
|
// +----------------------------------+ |
|
// |
|
// Afterwards, we create a clip path for each border side (CGContextSaveGState() |
|
// and CGContextRestoreGState() when drawing each side). The clip mask for each |
|
// segment is a trapezoid connecting corresponding edges of the inner and outer |
|
// rects. For example, in the case of the top edge, the points would be: |
|
// - (MinX(outer), MinY(outer)) |
|
// - (MaxX(outer), MinY(outer)) |
|
// - (MinX(inner) + topLeftRadius, MinY(inner) + topLeftRadius) |
|
// - (MaxX(inner) - topRightRadius, MinY(inner) + topRightRadius) |
|
// |
|
// +------------------+ |
|
// |\ /| |
|
// | \ / | |
|
// | \ top / | |
|
// | \ / | |
|
// | \ / | |
|
// | +------+ | |
|
// | | | | |
|
// | | | | |
|
// | | | | |
|
// |left | |right| |
|
// | | | | |
|
// | | | | |
|
// | +------+ | |
|
// | / \ | |
|
// | / \ | |
|
// | / \ | |
|
// | / bottom \ | |
|
// |/ \| |
|
// +------------------+ |
|
// |
|
// |
|
// Note that this approach will produce discontinous colour changes at the edge |
|
// (which is okay). The reason is that Quartz does not currently support drawing |
|
// of gradients _along_ a path (NB: clipping a path and drawing a linear gradient |
|
// is _not_ equivalent). |
|
|
|
static UIImage *RCTGetDashedOrDottedBorderImage(RCTBorderStyle borderStyle, |
|
RCTCornerRadii cornerRadii, |
|
CGSize viewSize, |
|
UIEdgeInsets borderInsets, |
|
RCTBorderColors borderColors, |
|
CGColorRef backgroundColor, |
|
BOOL drawToEdge) |
|
{ |
|
NSCParameterAssert(borderStyle == RCTBorderStyleDashed || borderStyle == RCTBorderStyleDotted); |
|
|
|
if (!RCTBorderColorsAreEqual(borderColors) || !RCTBorderInsetsAreEqual(borderInsets)) { |
|
RCTLogWarn(@"Unsupported dashed / dotted border style"); |
|
return nil; |
|
} |
|
|
|
const CGFloat lineWidth = borderInsets.top; |
|
if (lineWidth <= 0.0) { |
|
return nil; |
|
} |
|
|
|
const BOOL hasCornerRadii = RCTCornerRadiiAreAboveThreshold(cornerRadii); |
|
CGContextRef ctx = RCTUIGraphicsBeginImageContext(viewSize, backgroundColor, hasCornerRadii, drawToEdge); |
|
const CGRect rect = {.size = viewSize}; |
|
|
|
if (backgroundColor) { |
|
CGPathRef outerPath = RCTPathCreateOuterOutline(drawToEdge, rect, cornerRadii); |
|
CGContextAddPath(ctx, outerPath); |
|
CGPathRelease(outerPath); |
|
|
|
CGContextSetFillColorWithColor(ctx, backgroundColor); |
|
CGContextFillPath(ctx); |
|
} |
|
|
|
// Stroking means that the width is divided in half and grows in both directions |
|
// perpendicular to the path, that's why we inset by half the width, so that it |
|
// reaches the edge of the rect. |
|
CGRect pathRect = CGRectInset(rect, lineWidth / 2.0, lineWidth / 2.0); |
|
CGPathRef path = RCTPathCreateWithRoundedRect(pathRect, RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL); |
|
|
|
CGFloat dashLengths[2]; |
|
dashLengths[0] = dashLengths[1] = (borderStyle == RCTBorderStyleDashed ? 3 : 1) * lineWidth; |
|
|
|
CGContextSetLineWidth(ctx, lineWidth); |
|
CGContextSetLineDash(ctx, 0, dashLengths, sizeof(dashLengths) / sizeof(*dashLengths)); |
|
|
|
CGContextSetStrokeColorWithColor(ctx, [UIColor yellowColor].CGColor); |
|
|
|
CGContextAddPath(ctx, path); |
|
CGContextSetStrokeColorWithColor(ctx, borderColors.top); |
|
CGContextStrokePath(ctx); |
|
|
|
CGPathRelease(path); |
|
|
|
UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); |
|
UIGraphicsEndImageContext(); |
|
|
|
return image; |
|
} |
|
|
|
UIImage *RCTGetBorderImage(RCTBorderStyle borderStyle, |
|
CGSize viewSize, |
|
RCTCornerRadii cornerRadii, |
|
UIEdgeInsets borderInsets, |
|
RCTBorderColors borderColors, |
|
CGColorRef backgroundColor, |
|
BOOL drawToEdge) |
|
{ |
|
|
|
switch (borderStyle) { |
|
case RCTBorderStyleSolid: |
|
return RCTGetSolidBorderImage(cornerRadii, viewSize, borderInsets, borderColors, backgroundColor, drawToEdge); |
|
case RCTBorderStyleDashed: |
|
case RCTBorderStyleDotted: |
|
return RCTGetDashedOrDottedBorderImage(borderStyle, cornerRadii, viewSize, borderInsets, borderColors, backgroundColor, drawToEdge); |
|
case RCTBorderStyleUnset: |
|
break; |
|
} |
|
|
|
return nil; |
|
}
|
|
|