mirror of
https://github.com/status-im/react-native.git
synced 2025-02-24 15:18:10 +00:00
Implemented fast path for same borders/radii
This commit is contained in:
parent
3b24f52a20
commit
ff49d86aed
@ -81,11 +81,15 @@ var styles = StyleSheet.create({
|
||||
borderTopLeftRadius: 100,
|
||||
},
|
||||
border7: {
|
||||
borderRadius: 20,
|
||||
borderWidth: 10,
|
||||
borderColor: 'rgba(255,0,0,0.5)',
|
||||
borderRadius: 30,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
border7_inner: {
|
||||
backgroundColor: 'blue',
|
||||
flex: 1,
|
||||
width: 100,
|
||||
height: 100
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080241A694A8400A75B9A /* RCTWrapperViewController.m */; };
|
||||
13C156051AB1A2840079392D /* RCTWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13C156021AB1A2840079392D /* RCTWebView.m */; };
|
||||
13C156061AB1A2840079392D /* RCTWebViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13C156041AB1A2840079392D /* RCTWebViewManager.m */; };
|
||||
13CC8A821B17642100940AE7 /* RCTBorderDrawing.m in Sources */ = {isa = PBXBuildFile; fileRef = 13CC8A811B17642100940AE7 /* RCTBorderDrawing.m */; };
|
||||
13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067491A70F434002CDEE1 /* RCTUIManager.m */; };
|
||||
13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E0674C1A70F44B002CDEE1 /* RCTShadowView.m */; };
|
||||
13E067561A70F44B002CDEE1 /* RCTViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */; };
|
||||
@ -155,6 +156,8 @@
|
||||
13C325261AA63B6A0048765F /* RCTAutoInsetsProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAutoInsetsProtocol.h; sourceTree = "<group>"; };
|
||||
13C325271AA63B6A0048765F /* RCTScrollableProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTScrollableProtocol.h; sourceTree = "<group>"; };
|
||||
13C325281AA63B6A0048765F /* RCTViewNodeProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewNodeProtocol.h; sourceTree = "<group>"; };
|
||||
13CC8A801B17642100940AE7 /* RCTBorderDrawing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBorderDrawing.h; sourceTree = "<group>"; };
|
||||
13CC8A811B17642100940AE7 /* RCTBorderDrawing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBorderDrawing.m; sourceTree = "<group>"; };
|
||||
13E067481A70F434002CDEE1 /* RCTUIManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUIManager.h; sourceTree = "<group>"; };
|
||||
13E067491A70F434002CDEE1 /* RCTUIManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManager.m; sourceTree = "<group>"; };
|
||||
13E0674B1A70F44B002CDEE1 /* RCTShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowView.h; sourceTree = "<group>"; };
|
||||
@ -279,6 +282,8 @@
|
||||
children = (
|
||||
13442BF21AA90E0B0037E5B0 /* RCTAnimationType.h */,
|
||||
13C325261AA63B6A0048765F /* RCTAutoInsetsProtocol.h */,
|
||||
13CC8A801B17642100940AE7 /* RCTBorderDrawing.h */,
|
||||
13CC8A811B17642100940AE7 /* RCTBorderDrawing.m */,
|
||||
58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */,
|
||||
58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */,
|
||||
13456E911ADAD2DE009F94A7 /* RCTConvert+CoreLocation.h */,
|
||||
@ -513,6 +518,7 @@
|
||||
830A229E1A66C68A008503DA /* RCTRootView.m in Sources */,
|
||||
13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */,
|
||||
83CBBA5A1A601E9000E9B192 /* RCTRedBox.m in Sources */,
|
||||
13CC8A821B17642100940AE7 /* RCTBorderDrawing.m in Sources */,
|
||||
83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */,
|
||||
13AF20451AE707F9005F5298 /* RCTSlider.m in Sources */,
|
||||
58114A501AAE93D500E7D092 /* RCTAsyncLocalStorage.m in Sources */,
|
||||
|
61
React/Views/RCTBorderDrawing.h
Normal file
61
React/Views/RCTBorderDrawing.h
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef struct {
|
||||
CGFloat topLeft;
|
||||
CGFloat topRight;
|
||||
CGFloat bottomLeft;
|
||||
CGFloat bottomRight;
|
||||
} RCTCornerRadii;
|
||||
|
||||
typedef struct {
|
||||
CGSize topLeft;
|
||||
CGSize topRight;
|
||||
CGSize bottomLeft;
|
||||
CGSize bottomRight;
|
||||
} RCTCornerInsets;
|
||||
|
||||
typedef struct {
|
||||
CGColorRef top;
|
||||
CGColorRef left;
|
||||
CGColorRef bottom;
|
||||
CGColorRef right;
|
||||
} RCTBorderColors;
|
||||
|
||||
/**
|
||||
* Determine if the border widths, colors and radii are all equal.
|
||||
*/
|
||||
BOOL RCTBorderInsetsAreEqual(UIEdgeInsets borderInsets);
|
||||
BOOL RCTCornerRadiiAreEqual(RCTCornerRadii cornerRadii);
|
||||
BOOL RCTBorderColorsAreEqual(RCTBorderColors borderColors);
|
||||
|
||||
/**
|
||||
* Convert RCTCornerRadii to RCTCornerInsets by applying border insets.
|
||||
*/
|
||||
RCTCornerInsets RCTGetCornerInsets(RCTCornerRadii cornerRadii,
|
||||
UIEdgeInsets borderInsets);
|
||||
|
||||
/**
|
||||
* Create a CGPath representing a rounded rectangle with the specified bounds
|
||||
* and corner insets. Note that the CGPathRef must be released by the caller.
|
||||
*/
|
||||
CGPathRef RCTPathCreateWithRoundedRect(CGRect bounds,
|
||||
RCTCornerInsets cornerInsets,
|
||||
const CGAffineTransform *transform);
|
||||
|
||||
/**
|
||||
* Draw a CSS-compliant border as a scalable image.
|
||||
*/
|
||||
UIImage *RCTGetBorderImage(RCTCornerRadii cornerRadii,
|
||||
UIEdgeInsets borderInsets,
|
||||
RCTBorderColors borderColors,
|
||||
CGColorRef backgroundColor,
|
||||
BOOL drawToEdge);
|
331
React/Views/RCTBorderDrawing.m
Normal file
331
React/Views/RCTBorderDrawing.m
Normal file
@ -0,0 +1,331 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTBorderDrawing.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 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 = cornerInsets.topLeft;
|
||||
const CGSize topRight = cornerInsets.topRight;
|
||||
const CGSize bottomLeft = cornerInsets.bottomLeft;
|
||||
const CGSize bottomRight = cornerInsets.bottomRight;
|
||||
|
||||
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};
|
||||
}
|
||||
|
||||
UIImage *RCTGetBorderImage(RCTCornerRadii cornerRadii,
|
||||
UIEdgeInsets borderInsets,
|
||||
RCTBorderColors borderColors,
|
||||
CGColorRef backgroundColor,
|
||||
BOOL drawToEdge)
|
||||
{
|
||||
const BOOL hasCornerRadii =
|
||||
cornerRadii.topLeft > RCTViewBorderThreshold ||
|
||||
cornerRadii.topRight > RCTViewBorderThreshold ||
|
||||
cornerRadii.bottomLeft > RCTViewBorderThreshold ||
|
||||
cornerRadii.bottomRight > RCTViewBorderThreshold;
|
||||
|
||||
const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, borderInsets);
|
||||
|
||||
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 = (CGSize){
|
||||
edgeInsets.left + 1 + edgeInsets.right,
|
||||
edgeInsets.top + 1 + edgeInsets.bottom
|
||||
};
|
||||
|
||||
const CGFloat alpha = CGColorGetAlpha(backgroundColor);
|
||||
const BOOL opaque = (drawToEdge || !hasCornerRadii) && alpha == 1.0;
|
||||
UIGraphicsBeginImageContextWithOptions(size, opaque, 0.0);
|
||||
|
||||
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
||||
const CGRect rect = {.size = size};
|
||||
|
||||
CGPathRef path;
|
||||
if (drawToEdge) {
|
||||
path = CGPathCreateWithRect(rect, NULL);
|
||||
} else {
|
||||
path = RCTPathCreateWithRoundedRect(rect, RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL);
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
// RIGHT
|
||||
if (borderInsets.right > 0) {
|
||||
|
||||
const CGPoint points[] = {
|
||||
(CGPoint){size.width, 0},
|
||||
topRight,
|
||||
bottomRight,
|
||||
(CGPoint){size.width, size.height},
|
||||
};
|
||||
|
||||
CGContextSetFillColorWithColor(ctx, borderColors.right);
|
||||
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points));
|
||||
CGContextFillPath(ctx);
|
||||
}
|
||||
|
||||
// BOTTOM
|
||||
if (borderInsets.bottom > 0) {
|
||||
|
||||
const CGPoint points[] = {
|
||||
(CGPoint){0, size.height},
|
||||
bottomLeft,
|
||||
bottomRight,
|
||||
(CGPoint){size.width, size.height},
|
||||
};
|
||||
|
||||
CGContextSetFillColorWithColor(ctx, borderColors.bottom);
|
||||
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points));
|
||||
CGContextFillPath(ctx);
|
||||
}
|
||||
|
||||
// LEFT
|
||||
if (borderInsets.left > 0) {
|
||||
|
||||
const CGPoint points[] = {
|
||||
CGPointZero,
|
||||
topLeft,
|
||||
bottomLeft,
|
||||
(CGPoint){0, size.height},
|
||||
};
|
||||
|
||||
CGContextSetFillColorWithColor(ctx, borderColors.left);
|
||||
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points));
|
||||
CGContextFillPath(ctx);
|
||||
}
|
||||
|
||||
// TOP
|
||||
if (borderInsets.top > 0) {
|
||||
|
||||
const CGPoint points[] = {
|
||||
CGPointZero,
|
||||
topLeft,
|
||||
topRight,
|
||||
(CGPoint){size.width, 0},
|
||||
};
|
||||
|
||||
CGContextSetFillColorWithColor(ctx, borderColors.top);
|
||||
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points));
|
||||
CGContextFillPath(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
CGPathRelease(insetPath);
|
||||
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return [image resizableImageWithCapInsets:edgeInsets];
|
||||
}
|
@ -10,13 +10,12 @@
|
||||
#import "RCTView.h"
|
||||
|
||||
#import "RCTAutoInsetsProtocol.h"
|
||||
#import "RCTBorderDrawing.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "UIView+React.h"
|
||||
|
||||
static const CGFloat RCTViewBorderThreshold = 0.001;
|
||||
|
||||
static UIView *RCTViewHitTest(UIView *view, CGPoint point, UIEvent *event)
|
||||
{
|
||||
for (UIView *subview in [view.subviews reverseObjectEnumerator]) {
|
||||
@ -31,10 +30,6 @@ static UIView *RCTViewHitTest(UIView *view, CGPoint point, UIEvent *event)
|
||||
return nil;
|
||||
}
|
||||
|
||||
static BOOL RCTEllipseGetIntersectionsWithLine(CGRect ellipseBoundingRect, CGPoint p1, CGPoint p2, CGPoint intersections[2]);
|
||||
static CGPathRef RCTPathCreateWithRoundedRect(CGRect rect, CGFloat topLeftRadiusX, CGFloat topLeftRadiusY, CGFloat topRightRadiusX, CGFloat topRightRadiusY, CGFloat bottomLeftRadiusX, CGFloat bottomLeftRadiusY, CGFloat bottomRightRadiusX, CGFloat bottomRightRadiusY, const CGAffineTransform *transform);
|
||||
static void RCTPathAddEllipticArc(CGMutablePathRef path, const CGAffineTransform *m, CGFloat x, CGFloat y, CGFloat xRadius, CGFloat yRadius, CGFloat startAngle, CGFloat endAngle, bool clockwise);
|
||||
|
||||
@implementation UIView (RCTViewUnmounting)
|
||||
|
||||
- (void)react_remountAllSubviews
|
||||
@ -443,246 +438,88 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
||||
[self.layer setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (UIImage *)borderImage:(out CGRect *)contentsCenter
|
||||
- (UIEdgeInsets)bordersAsInsets
|
||||
{
|
||||
const CGFloat maxRadius = ({
|
||||
const CGRect bounds = self.bounds;
|
||||
MIN(bounds.size.height, bounds.size.width);
|
||||
});
|
||||
|
||||
const CGFloat radius = MAX(0, _borderRadius);
|
||||
const CGFloat topLeftRadius = MIN(_borderTopLeftRadius >= 0 ? _borderTopLeftRadius : radius, maxRadius);
|
||||
const CGFloat topRightRadius = MIN(_borderTopRightRadius >= 0 ? _borderTopRightRadius : radius, maxRadius);
|
||||
const CGFloat bottomLeftRadius = MIN(_borderBottomLeftRadius >= 0 ? _borderBottomLeftRadius : radius, maxRadius);
|
||||
const CGFloat bottomRightRadius = MIN(_borderBottomRightRadius >= 0 ? _borderBottomRightRadius : radius, maxRadius);
|
||||
|
||||
const CGFloat borderWidth = MAX(0, _borderWidth);
|
||||
const CGFloat topWidth = _borderTopWidth >= 0 ? _borderTopWidth : borderWidth;
|
||||
const CGFloat rightWidth = _borderRightWidth >= 0 ? _borderRightWidth : borderWidth;
|
||||
const CGFloat bottomWidth = _borderBottomWidth >= 0 ? _borderBottomWidth : borderWidth;
|
||||
const CGFloat leftWidth = _borderLeftWidth >= 0 ? _borderLeftWidth : borderWidth;
|
||||
|
||||
const BOOL hasCornerRadii =
|
||||
topLeftRadius > RCTViewBorderThreshold ||
|
||||
topRightRadius > RCTViewBorderThreshold ||
|
||||
bottomLeftRadius > RCTViewBorderThreshold ||
|
||||
bottomRightRadius > RCTViewBorderThreshold;
|
||||
return (UIEdgeInsets) {
|
||||
_borderTopWidth >= 0 ? _borderTopWidth : borderWidth,
|
||||
_borderLeftWidth >= 0 ? _borderLeftWidth : borderWidth,
|
||||
_borderBottomWidth >= 0 ? _borderBottomWidth : borderWidth,
|
||||
_borderRightWidth >= 0 ? _borderRightWidth : borderWidth,
|
||||
};
|
||||
}
|
||||
|
||||
const BOOL hasBorders =
|
||||
topWidth > RCTViewBorderThreshold ||
|
||||
rightWidth > RCTViewBorderThreshold ||
|
||||
bottomWidth > RCTViewBorderThreshold ||
|
||||
leftWidth > RCTViewBorderThreshold;
|
||||
- (RCTCornerRadii)cornerRadii
|
||||
{
|
||||
const CGRect bounds = self.bounds;
|
||||
const CGFloat maxRadius = MIN(bounds.size.height, bounds.size.width);
|
||||
const CGFloat radius = MAX(0, _borderRadius);
|
||||
|
||||
if (!hasCornerRadii && !hasBorders) {
|
||||
return nil;
|
||||
}
|
||||
return (RCTCornerRadii){
|
||||
MIN(_borderTopLeftRadius >= 0 ? _borderTopLeftRadius : radius, maxRadius),
|
||||
MIN(_borderTopRightRadius >= 0 ? _borderTopRightRadius : radius, maxRadius),
|
||||
MIN(_borderBottomLeftRadius >= 0 ? _borderBottomLeftRadius : radius, maxRadius),
|
||||
MIN(_borderBottomRightRadius >= 0 ? _borderBottomRightRadius : radius, maxRadius),
|
||||
};
|
||||
}
|
||||
|
||||
const CGFloat innerTopLeftRadiusX = MAX(0, topLeftRadius - leftWidth);
|
||||
const CGFloat innerTopLeftRadiusY = MAX(0, topLeftRadius - topWidth);
|
||||
|
||||
const CGFloat innerTopRightRadiusX = MAX(0, topRightRadius - rightWidth);
|
||||
const CGFloat innerTopRightRadiusY = MAX(0, topRightRadius - topWidth);
|
||||
|
||||
const CGFloat innerBottomLeftRadiusX = MAX(0, bottomLeftRadius - leftWidth);
|
||||
const CGFloat innerBottomLeftRadiusY = MAX(0, bottomLeftRadius - bottomWidth);
|
||||
|
||||
const CGFloat innerBottomRightRadiusX = MAX(0, bottomRightRadius - rightWidth);
|
||||
const CGFloat innerBottomRightRadiusY = MAX(0, bottomRightRadius - bottomWidth);
|
||||
|
||||
const UIEdgeInsets edgeInsets = UIEdgeInsetsMake(topWidth + MAX(innerTopLeftRadiusY, innerTopRightRadiusY), leftWidth + MAX(innerTopLeftRadiusX, innerBottomLeftRadiusX), bottomWidth + MAX(innerBottomLeftRadiusY, innerBottomRightRadiusY), rightWidth + + MAX(innerBottomRightRadiusX, innerTopRightRadiusX));
|
||||
const CGSize size = CGSizeMake(edgeInsets.left + 1 + edgeInsets.right, edgeInsets.top + 1 + edgeInsets.bottom);
|
||||
|
||||
const CGFloat alpha = CGColorGetAlpha(_backgroundColor.CGColor);
|
||||
const BOOL opaque = (self.clipsToBounds || !hasCornerRadii) && alpha == 1.0;
|
||||
UIGraphicsBeginImageContextWithOptions(size, opaque, 0.0);
|
||||
|
||||
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
||||
const CGRect rect = {.size = size};
|
||||
|
||||
CGPathRef path;
|
||||
const BOOL hasClipping = self.clipsToBounds;
|
||||
if (hasClipping) {
|
||||
path = CGPathCreateWithRect(rect, NULL);
|
||||
} else {
|
||||
path = RCTPathCreateWithRoundedRect(rect, topLeftRadius, topLeftRadius, topRightRadius, topRightRadius, bottomLeftRadius, bottomLeftRadius, bottomRightRadius, bottomRightRadius, NULL);
|
||||
}
|
||||
|
||||
if (_backgroundColor) {
|
||||
CGContextSaveGState(ctx);
|
||||
|
||||
CGContextSetFillColorWithColor(ctx, _backgroundColor.CGColor);
|
||||
CGContextAddPath(ctx, path);
|
||||
CGContextFillPath(ctx);
|
||||
|
||||
CGContextRestoreGState(ctx);
|
||||
}
|
||||
|
||||
CGContextAddPath(ctx, path);
|
||||
CGPathRelease(path);
|
||||
|
||||
const BOOL hasRadius = topLeftRadius > 0 || topRightRadius > 0 || bottomLeftRadius > 0 || bottomRightRadius > 0;
|
||||
const UIEdgeInsets insetEdgeInsets = UIEdgeInsetsMake(topWidth, leftWidth, bottomWidth, rightWidth);
|
||||
CGPathRef insetPath = RCTPathCreateWithRoundedRect(UIEdgeInsetsInsetRect(rect, insetEdgeInsets), innerTopLeftRadiusX, innerTopLeftRadiusY, innerTopRightRadiusX, innerTopRightRadiusY, innerBottomLeftRadiusX, innerBottomLeftRadiusY, innerBottomRightRadiusX, innerBottomRightRadiusY, NULL);
|
||||
|
||||
CGContextAddPath(ctx, insetPath);
|
||||
CGContextEOClip(ctx);
|
||||
|
||||
BOOL hasEqualColor = !_borderTopColor && !_borderRightColor && !_borderBottomColor && !_borderLeftColor;
|
||||
if ((hasClipping || !hasRadius) && hasEqualColor) {
|
||||
CGContextSetFillColorWithColor(ctx, _borderColor);
|
||||
CGContextAddRect(ctx, rect);
|
||||
CGContextAddPath(ctx, insetPath);
|
||||
CGContextEOFillPath(ctx);
|
||||
} else {
|
||||
BOOL didSet = NO;
|
||||
CGPoint topLeft;
|
||||
if (innerTopLeftRadiusX > 0 && innerTopLeftRadiusY > 0) {
|
||||
CGPoint points[2];
|
||||
RCTEllipseGetIntersectionsWithLine(CGRectMake(leftWidth, topWidth, 2 * innerTopLeftRadiusX, 2 * innerTopLeftRadiusY), CGPointMake(0, 0), CGPointMake(leftWidth, topWidth), points);
|
||||
if (!isnan(points[1].x) && !isnan(points[1].y)) {
|
||||
topLeft = points[1];
|
||||
didSet = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (!didSet) {
|
||||
topLeft = CGPointMake(leftWidth, topWidth);
|
||||
}
|
||||
|
||||
didSet = NO;
|
||||
CGPoint bottomLeft;
|
||||
if (innerBottomLeftRadiusX > 0 && innerBottomLeftRadiusY > 0) {
|
||||
CGPoint points[2];
|
||||
RCTEllipseGetIntersectionsWithLine(CGRectMake(leftWidth, (size.height - bottomWidth) - 2 * innerBottomLeftRadiusY, 2 * innerBottomLeftRadiusX, 2 * innerBottomLeftRadiusY), CGPointMake(0, size.height), CGPointMake(leftWidth, size.height - bottomWidth), points);
|
||||
if (!isnan(points[1].x) && !isnan(points[1].y)) {
|
||||
bottomLeft = points[1];
|
||||
didSet = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (!didSet) {
|
||||
bottomLeft = CGPointMake(leftWidth, size.height - bottomWidth);
|
||||
}
|
||||
|
||||
didSet = NO;
|
||||
CGPoint topRight;
|
||||
if (innerTopRightRadiusX > 0 && innerTopRightRadiusY > 0) {
|
||||
CGPoint points[2];
|
||||
RCTEllipseGetIntersectionsWithLine(CGRectMake((size.width - rightWidth) - 2 * innerTopRightRadiusX, topWidth, 2 * innerTopRightRadiusX, 2 * innerTopRightRadiusY), CGPointMake(size.width, 0), CGPointMake(size.width - rightWidth, topWidth), points);
|
||||
if (!isnan(points[0].x) && !isnan(points[0].y)) {
|
||||
topRight = points[0];
|
||||
didSet = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (!didSet) {
|
||||
topRight = CGPointMake(size.width - rightWidth, topWidth);
|
||||
}
|
||||
|
||||
didSet = NO;
|
||||
CGPoint bottomRight;
|
||||
if (innerBottomRightRadiusX > 0 && innerBottomRightRadiusY > 0) {
|
||||
CGPoint points[2];
|
||||
RCTEllipseGetIntersectionsWithLine(CGRectMake((size.width - rightWidth) - 2 * innerBottomRightRadiusX, (size.height - bottomWidth) - 2 * innerBottomRightRadiusY, 2 * innerBottomRightRadiusX, 2 * innerBottomRightRadiusY), CGPointMake(size.width, size.height), CGPointMake(size.width - rightWidth, size.height - bottomWidth), points);
|
||||
if (!isnan(points[0].x) && !isnan(points[0].y)) {
|
||||
bottomRight = points[0];
|
||||
didSet = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (!didSet) {
|
||||
bottomRight = CGPointMake(size.width - rightWidth, size.height - bottomWidth);
|
||||
}
|
||||
|
||||
// RIGHT
|
||||
if (rightWidth > 0) {
|
||||
CGContextSaveGState(ctx);
|
||||
|
||||
const CGPoint points[] = {
|
||||
CGPointMake(size.width, 0),
|
||||
topRight,
|
||||
bottomRight,
|
||||
CGPointMake(size.width, size.height),
|
||||
};
|
||||
|
||||
CGContextSetFillColorWithColor(ctx, _borderRightColor ?: _borderColor);
|
||||
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points));
|
||||
CGContextFillPath(ctx);
|
||||
|
||||
CGContextRestoreGState(ctx);
|
||||
}
|
||||
|
||||
// BOTTOM
|
||||
if (bottomWidth > 0) {
|
||||
CGContextSaveGState(ctx);
|
||||
|
||||
const CGPoint points[] = {
|
||||
CGPointMake(0, size.height),
|
||||
bottomLeft,
|
||||
bottomRight,
|
||||
CGPointMake(size.width, size.height),
|
||||
};
|
||||
|
||||
CGContextSetFillColorWithColor(ctx, _borderBottomColor ?: _borderColor);
|
||||
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points));
|
||||
CGContextFillPath(ctx);
|
||||
|
||||
CGContextRestoreGState(ctx);
|
||||
}
|
||||
|
||||
// LEFT
|
||||
if (leftWidth > 0) {
|
||||
CGContextSaveGState(ctx);
|
||||
|
||||
const CGPoint points[] = {
|
||||
CGPointMake(0, 0),
|
||||
topLeft,
|
||||
bottomLeft,
|
||||
CGPointMake(0, size.height),
|
||||
};
|
||||
|
||||
CGContextSetFillColorWithColor(ctx, _borderLeftColor ?: _borderColor);
|
||||
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points));
|
||||
CGContextFillPath(ctx);
|
||||
|
||||
CGContextRestoreGState(ctx);
|
||||
}
|
||||
|
||||
// TOP
|
||||
if (topWidth > 0) {
|
||||
CGContextSaveGState(ctx);
|
||||
|
||||
const CGPoint points[] = {
|
||||
CGPointMake(0, 0),
|
||||
topLeft,
|
||||
topRight,
|
||||
CGPointMake(size.width, 0),
|
||||
};
|
||||
|
||||
CGContextSetFillColorWithColor(ctx, _borderTopColor ?: _borderColor);
|
||||
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points));
|
||||
CGContextFillPath(ctx);
|
||||
|
||||
CGContextRestoreGState(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
CGPathRelease(insetPath);
|
||||
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
*contentsCenter = CGRectMake(edgeInsets.left / size.width, edgeInsets.top / size.height, 1.0 / size.width, 1.0 / size.height);
|
||||
return [image resizableImageWithCapInsets:edgeInsets];
|
||||
- (RCTBorderColors)borderColors
|
||||
{
|
||||
return (RCTBorderColors){
|
||||
_borderTopColor ?: _borderColor,
|
||||
_borderLeftColor ?: _borderColor,
|
||||
_borderBottomColor ?: _borderColor,
|
||||
_borderRightColor ?: _borderColor
|
||||
};
|
||||
}
|
||||
|
||||
- (void)displayLayer:(CALayer *)layer
|
||||
{
|
||||
CGRect contentsCenter = {.size = {1, 1}};
|
||||
UIImage *image = [self borderImage:&contentsCenter];
|
||||
const RCTCornerRadii cornerRadii = [self cornerRadii];
|
||||
const UIEdgeInsets borderInsets = [self bordersAsInsets];
|
||||
const RCTBorderColors borderColors = [self borderColors];
|
||||
|
||||
if (image && RCTRunningInTestEnvironment()) {
|
||||
BOOL useIOSBorderRendering =
|
||||
!RCTRunningInTestEnvironment() &&
|
||||
RCTCornerRadiiAreEqual(cornerRadii) &&
|
||||
RCTBorderInsetsAreEqual(borderInsets) &&
|
||||
RCTBorderColorsAreEqual(borderColors);
|
||||
|
||||
// TODO: A problem with this is that iOS draws borders in front of the content
|
||||
// whereas CSS draws them behind the content. Also iOS clips to the outside of
|
||||
// the border, but CSS clips to the inside. To solve this, we'll need to add
|
||||
// a container view inside the main view to correctly clip the subviews.
|
||||
|
||||
if (useIOSBorderRendering) {
|
||||
layer.cornerRadius = cornerRadii.topLeft;
|
||||
layer.borderColor = borderColors.left;
|
||||
layer.borderWidth = borderInsets.left;
|
||||
layer.backgroundColor = _backgroundColor.CGColor;
|
||||
layer.contents = nil;
|
||||
layer.needsDisplayOnBoundsChange = NO;
|
||||
layer.mask = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
UIImage *image = RCTGetBorderImage([self cornerRadii],
|
||||
[self bordersAsInsets],
|
||||
[self borderColors],
|
||||
_backgroundColor.CGColor,
|
||||
self.clipsToBounds);
|
||||
|
||||
const CGRect contentsCenter = ({
|
||||
CGSize size = image.size;
|
||||
UIEdgeInsets insets = image.capInsets;
|
||||
CGRectMake(
|
||||
insets.left / size.width,
|
||||
insets.top / size.height,
|
||||
1.0 / size.width,
|
||||
1.0 / size.height
|
||||
);
|
||||
});
|
||||
|
||||
if (RCTRunningInTestEnvironment()) {
|
||||
const CGSize size = self.bounds.size;
|
||||
UIGraphicsBeginImageContextWithOptions(size, NO, image.scale);
|
||||
[image drawInRect:(CGRect){CGPointZero, size}];
|
||||
@ -690,12 +527,12 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
||||
UIGraphicsEndImageContext();
|
||||
}
|
||||
|
||||
layer.backgroundColor = [image ? [UIColor clearColor] : _backgroundColor CGColor];
|
||||
layer.backgroundColor = NULL;
|
||||
layer.contents = (id)image.CGImage;
|
||||
layer.contentsCenter = contentsCenter;
|
||||
layer.contentsScale = image.scale ?: 1.0;
|
||||
layer.contentsScale = image.scale;
|
||||
layer.magnificationFilter = kCAFilterNearest;
|
||||
layer.needsDisplayOnBoundsChange = image != nil;
|
||||
layer.needsDisplayOnBoundsChange = YES;
|
||||
|
||||
[self updateClippingForLayer:layer];
|
||||
}
|
||||
@ -706,30 +543,19 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
||||
CGFloat cornerRadius = 0;
|
||||
|
||||
if (self.clipsToBounds) {
|
||||
if (_borderRadius > 0 && _borderTopLeftRadius < 0 && _borderTopRightRadius < 0 && _borderBottomLeftRadius < 0 && _borderBottomRightRadius < 0) {
|
||||
cornerRadius = _borderRadius;
|
||||
|
||||
const RCTCornerRadii cornerRadii = [self cornerRadii];
|
||||
if (RCTCornerRadiiAreEqual(cornerRadii)) {
|
||||
|
||||
cornerRadius = cornerRadii.topLeft;
|
||||
|
||||
} else {
|
||||
const CGRect bounds = layer.bounds;
|
||||
const CGFloat maxRadius = MIN(bounds.size.height, bounds.size.width);
|
||||
const CGFloat radius = MAX(0, _borderRadius);
|
||||
const CGFloat topLeftRadius = MIN(_borderTopLeftRadius >= 0 ? _borderTopLeftRadius : radius, maxRadius);
|
||||
const CGFloat topRightRadius = MIN(_borderTopRightRadius >= 0 ? _borderTopRightRadius : radius, maxRadius);
|
||||
const CGFloat bottomLeftRadius = MIN(_borderBottomLeftRadius >= 0 ? _borderBottomLeftRadius : radius, maxRadius);
|
||||
const CGFloat bottomRightRadius = MIN(_borderBottomRightRadius >= 0 ? _borderBottomRightRadius : radius, maxRadius);
|
||||
|
||||
if (ABS(topLeftRadius - topRightRadius) < RCTViewBorderThreshold &&
|
||||
ABS(topLeftRadius - bottomLeftRadius) < RCTViewBorderThreshold &&
|
||||
ABS(topLeftRadius - bottomRightRadius) < RCTViewBorderThreshold) {
|
||||
cornerRadius = topLeftRadius;
|
||||
} else {
|
||||
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
|
||||
|
||||
CGPathRef path = RCTPathCreateWithRoundedRect(bounds, topLeftRadius, topLeftRadius, topRightRadius, topRightRadius, bottomLeftRadius, bottomLeftRadius, bottomRightRadius, bottomRightRadius, NULL);
|
||||
shapeLayer.path = path;
|
||||
CGPathRelease(path);
|
||||
|
||||
mask = shapeLayer;
|
||||
}
|
||||
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
|
||||
CGPathRef path = RCTPathCreateWithRoundedRect(self.bounds, RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL);
|
||||
shapeLayer.path = path;
|
||||
CGPathRelease(path);
|
||||
mask = shapeLayer;
|
||||
}
|
||||
}
|
||||
|
||||
@ -790,74 +616,3 @@ setBorderRadius(BottomLeft)
|
||||
setBorderRadius(BottomRight)
|
||||
|
||||
@end
|
||||
|
||||
static void RCTPathAddEllipticArc(CGMutablePathRef path, const CGAffineTransform *m, CGFloat x, CGFloat y, CGFloat xRadius, CGFloat yRadius, CGFloat startAngle, CGFloat endAngle, bool clockwise)
|
||||
{
|
||||
CGFloat xScale = 1, yScale = 1, radius = 0;
|
||||
if (xRadius != 0) {
|
||||
xScale = 1;
|
||||
yScale = yRadius / xRadius;
|
||||
radius = xRadius;
|
||||
} else if (yRadius != 0) {
|
||||
xScale = xRadius / yRadius;
|
||||
yScale = 1;
|
||||
radius = yRadius;
|
||||
}
|
||||
|
||||
CGAffineTransform t = CGAffineTransformMakeTranslation(x, y);
|
||||
t = CGAffineTransformScale(t, xScale, yScale);
|
||||
if (m != NULL) {
|
||||
t = CGAffineTransformConcat(t, *m);
|
||||
}
|
||||
|
||||
CGPathAddArc(path, &t, 0, 0, radius, startAngle, endAngle, clockwise);
|
||||
}
|
||||
|
||||
static CGPathRef RCTPathCreateWithRoundedRect(CGRect rect, CGFloat topLeftRadiusX, CGFloat topLeftRadiusY, CGFloat topRightRadiusX, CGFloat topRightRadiusY, CGFloat bottomLeftRadiusX, CGFloat bottomLeftRadiusY, CGFloat bottomRightRadiusX, CGFloat bottomRightRadiusY, const CGAffineTransform *transform)
|
||||
{
|
||||
const CGFloat minX = CGRectGetMinX(rect);
|
||||
const CGFloat minY = CGRectGetMinY(rect);
|
||||
const CGFloat maxX = CGRectGetMaxX(rect);
|
||||
const CGFloat maxY = CGRectGetMaxY(rect);
|
||||
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
RCTPathAddEllipticArc(path, transform, minX + topLeftRadiusX, minY + topLeftRadiusY, topLeftRadiusX, topLeftRadiusY, M_PI, 3 * M_PI_2, false);
|
||||
RCTPathAddEllipticArc(path, transform, maxX - topRightRadiusX, minY + topRightRadiusY, topRightRadiusX, topRightRadiusY, 3 * M_PI_2, 0, false);
|
||||
RCTPathAddEllipticArc(path, transform, maxX - bottomRightRadiusX, maxY - bottomRightRadiusY, bottomRightRadiusX, bottomRightRadiusY, 0, M_PI_2, false);
|
||||
RCTPathAddEllipticArc(path, transform, minX + bottomLeftRadiusX, maxY - bottomLeftRadiusY, bottomLeftRadiusX, bottomLeftRadiusY, M_PI_2, M_PI, false);
|
||||
CGPathCloseSubpath(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
static BOOL RCTEllipseGetIntersectionsWithLine(CGRect ellipseBoundingRect, CGPoint p1, CGPoint p2, CGPoint intersections[2])
|
||||
{
|
||||
const CGFloat ellipseCenterX = CGRectGetMidX(ellipseBoundingRect);
|
||||
const CGFloat ellipseCenterY = CGRectGetMidY(ellipseBoundingRect);
|
||||
|
||||
// ellipseBoundingRect.origin.x -= ellipseCenterX;
|
||||
// ellipseBoundingRect.origin.y -= ellipseCenterY;
|
||||
|
||||
p1.x -= ellipseCenterX;
|
||||
p1.y -= ellipseCenterY;
|
||||
|
||||
p2.x -= ellipseCenterX;
|
||||
p2.y -= ellipseCenterY;
|
||||
|
||||
const CGFloat m = (p2.y - p1.y) / (p2.x - p1.x);
|
||||
const CGFloat a = ellipseBoundingRect.size.width / 2;
|
||||
const CGFloat b = ellipseBoundingRect.size.height / 2;
|
||||
const CGFloat c = p1.y - m * p1.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] = CGPointMake(x1 + ellipseCenterX, y1 + ellipseCenterY);
|
||||
intersections[1] = CGPointMake(x2 + ellipseCenterX, y2 + ellipseCenterY);
|
||||
return YES;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user