mirror of
https://github.com/zoriya/react-native-svg.git
synced 2026-06-05 07:59:28 +00:00
Merge branch 'ios-touch-CGPathContainsPoint-bug'
This commit is contained in:
@@ -28,6 +28,8 @@ import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.facebook.react.views.text.ReactFontManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.graphics.Matrix.MTRANS_X;
|
||||
@@ -47,6 +49,8 @@ class TSpanView extends TextView {
|
||||
|
||||
@Nullable String mContent;
|
||||
private TextPathView textPath;
|
||||
ArrayList<String> emoji = new ArrayList<>();
|
||||
ArrayList<Matrix> emojiTransforms = new ArrayList<>();
|
||||
|
||||
public TSpanView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
@@ -61,6 +65,20 @@ class TSpanView extends TextView {
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
if (mContent != null) {
|
||||
int numEmoji = emoji.size();
|
||||
if (numEmoji > 0) {
|
||||
GlyphContext gc = getTextRootGlyphContext();
|
||||
FontData font = gc.getFont();
|
||||
applyTextPropertiesToPaint(paint, font);
|
||||
for (int i = 0; i < numEmoji; i++) {
|
||||
String current = emoji.get(i);
|
||||
Matrix mid = emojiTransforms.get(i);
|
||||
canvas.save();
|
||||
canvas.concat(mid);
|
||||
canvas.drawText(current, 0, 0, paint);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
drawPath(canvas, paint, opacity);
|
||||
} else {
|
||||
clip(canvas, paint);
|
||||
@@ -676,6 +694,9 @@ class TSpanView extends TextView {
|
||||
final float[] midPointMatrixData = new float[9];
|
||||
final float[] endPointMatrixData = new float[9];
|
||||
|
||||
emoji.clear();
|
||||
emojiTransforms.clear();
|
||||
|
||||
for (int index = 0; index < length; index++) {
|
||||
char currentChar = chars[index];
|
||||
String current = String.valueOf(currentChar);
|
||||
@@ -855,12 +876,12 @@ class TSpanView extends TextView {
|
||||
glyph.computeBounds(bounds, true);
|
||||
float width = bounds.width();
|
||||
if (width == 0) { // Render unicode emoji
|
||||
mid.getValues(midPointMatrixData);
|
||||
double midX = midPointMatrixData[MTRANS_X];
|
||||
double midY = midPointMatrixData[MTRANS_Y];
|
||||
canvas.rotate((float) r, (float)midX, (float)midY);
|
||||
canvas.drawText(current, (float)midX, (float)midY, paint);
|
||||
canvas.rotate((float) -r, (float)midX, (float)midY);
|
||||
canvas.save();
|
||||
canvas.concat(mid);
|
||||
emoji.add(current);
|
||||
emojiTransforms.add(new Matrix(mid));
|
||||
canvas.drawText(current, 0, 0, paint);
|
||||
canvas.restore();
|
||||
} else {
|
||||
glyph.transform(mid);
|
||||
path.addPath(glyph);
|
||||
|
||||
@@ -74,9 +74,13 @@
|
||||
|
||||
return YES;
|
||||
}];
|
||||
[self setHitArea:[self getPath:context]];
|
||||
CGPathRef path = [self getPath:context];
|
||||
[self setHitArea:path];
|
||||
if (!CGRectEqualToRect(bounds, CGRectNull)) {
|
||||
self.clientRect = bounds;
|
||||
const CGRect fillBounds = CGPathGetBoundingBox(path);
|
||||
const CGRect strokeBounds = CGPathGetBoundingBox(self.strokePath);
|
||||
self.pathBounds = CGRectUnion(fillBounds, strokeBounds);
|
||||
|
||||
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
|
||||
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
|
||||
@@ -150,6 +154,10 @@
|
||||
CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix);
|
||||
transformed = CGPointApplyAffineTransform(transformed, self.invTransform);
|
||||
|
||||
if (!CGRectContainsPoint(self.pathBounds, transformed)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (self.clipPath) {
|
||||
RNSVGClipPath *clipNode = (RNSVGClipPath*)[self.svgView getDefinedClipPath:self.clipPath];
|
||||
if ([clipNode isSimpleClipPath]) {
|
||||
|
||||
@@ -117,6 +117,7 @@
|
||||
CGPathRef hitAreaPath = CGPathCreateWithRect(hitArea, nil);
|
||||
[self setHitArea:hitAreaPath];
|
||||
CGPathRelease(hitAreaPath);
|
||||
self.pathBounds = hitArea;
|
||||
|
||||
// apply viewBox transform on Image render.
|
||||
CGRect imageBounds = CGRectMake(0, 0, _imageSize.width, _imageSize.height);
|
||||
|
||||
@@ -40,7 +40,9 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE;
|
||||
@property (nonatomic, assign) BOOL dirty;
|
||||
@property (nonatomic, assign) BOOL merging;
|
||||
@property (nonatomic, assign) CGPathRef path;
|
||||
@property (nonatomic, assign) CGPathRef strokePath;
|
||||
@property (nonatomic, assign) CGRect clientRect;
|
||||
@property (nonatomic, assign) CGRect pathBounds;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onLayout;
|
||||
|
||||
|
||||
|
||||
@@ -550,6 +550,7 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12;
|
||||
- (void)dealloc
|
||||
{
|
||||
CGPathRelease(_cachedClipPath);
|
||||
CGPathRelease(_strokePath);
|
||||
CGImageRelease(_clipMask);
|
||||
CGPathRelease(_path);
|
||||
_clipMask = nil;
|
||||
|
||||
+16
-14
@@ -18,7 +18,6 @@
|
||||
NSArray<NSString *> *_attributeList;
|
||||
NSArray<RNSVGLength *> *_sourceStrokeDashArray;
|
||||
CGFloat *_strokeDashArrayData;
|
||||
CGPathRef _strokePath;
|
||||
CGPathRef _srcHitPath;
|
||||
CGPathRef _hitArea;
|
||||
}
|
||||
@@ -157,7 +156,6 @@
|
||||
- (void)dealloc
|
||||
{
|
||||
CGPathRelease(_hitArea);
|
||||
CGPathRelease(_strokePath);
|
||||
_sourceStrokeDashArray = nil;
|
||||
if (_strokeDashArrayData) {
|
||||
free(_strokeDashArrayData);
|
||||
@@ -295,15 +293,15 @@ UInt32 saturate(CGFloat value) {
|
||||
self.path = CGPathRetain(path);
|
||||
}
|
||||
[self setHitArea:path];
|
||||
const CGRect fillBounds = CGPathGetBoundingBox(path);
|
||||
const CGRect strokeBounds = CGPathGetBoundingBox(self.strokePath);
|
||||
self.pathBounds = CGRectUnion(fillBounds, strokeBounds);
|
||||
}
|
||||
|
||||
const CGRect fillBounds = CGPathGetBoundingBox(path);
|
||||
const CGRect strokeBounds = CGPathGetBoundingBox(_strokePath);
|
||||
const CGRect pathBounding = CGRectUnion(fillBounds, strokeBounds);
|
||||
const CGRect pathBounds = self.pathBounds;
|
||||
|
||||
CGAffineTransform current = CGContextGetCTM(context);
|
||||
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
|
||||
CGRect clientRect = CGRectApplyAffineTransform(pathBounding, svgToClientTransform);
|
||||
CGRect clientRect = CGRectApplyAffineTransform(pathBounds, svgToClientTransform);
|
||||
|
||||
self.clientRect = clientRect;
|
||||
|
||||
@@ -312,7 +310,7 @@ UInt32 saturate(CGFloat value) {
|
||||
CGAffineTransform matrix = CGAffineTransformConcat(transform, vbmatrix);
|
||||
|
||||
CGRect bounds = CGRectMake(0, 0, CGRectGetWidth(clientRect), CGRectGetHeight(clientRect));
|
||||
CGPoint mid = CGPointMake(CGRectGetMidX(pathBounding), CGRectGetMidY(pathBounding));
|
||||
CGPoint mid = CGPointMake(CGRectGetMidX(pathBounds), CGRectGetMidY(pathBounds));
|
||||
CGPoint center = CGPointApplyAffineTransform(mid, matrix);
|
||||
|
||||
self.bounds = bounds;
|
||||
@@ -352,7 +350,7 @@ UInt32 saturate(CGFloat value) {
|
||||
[self.fill paint:context
|
||||
opacity:self.fillOpacity
|
||||
painter:[self.svgView getDefinedPainter:self.fill.brushRef]
|
||||
bounds:pathBounding
|
||||
bounds:pathBounds
|
||||
];
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
@@ -409,7 +407,7 @@ UInt32 saturate(CGFloat value) {
|
||||
[self.stroke paint:context
|
||||
opacity:self.strokeOpacity
|
||||
painter:[self.svgView getDefinedPainter:self.stroke.brushRef]
|
||||
bounds:pathBounding
|
||||
bounds:pathBounds
|
||||
];
|
||||
return;
|
||||
}
|
||||
@@ -426,13 +424,13 @@ UInt32 saturate(CGFloat value) {
|
||||
}
|
||||
_srcHitPath = path;
|
||||
CGPathRelease(_hitArea);
|
||||
CGPathRelease(_strokePath);
|
||||
CGPathRelease(self.strokePath);
|
||||
_hitArea = CGPathCreateCopy(path);
|
||||
_strokePath = nil;
|
||||
self.strokePath = nil;
|
||||
if (self.stroke && self.strokeWidth) {
|
||||
// Add stroke to hitArea
|
||||
CGFloat width = [self relativeOnOther:self.strokeWidth];
|
||||
_strokePath = CGPathRetain(CFAutorelease(CGPathCreateCopyByStrokingPath(path, nil, width, self.strokeLinecap, self.strokeLinejoin, self.strokeMiterlimit)));
|
||||
self.strokePath = CGPathRetain(CFAutorelease(CGPathCreateCopyByStrokingPath(path, nil, width, self.strokeLinecap, self.strokeLinejoin, self.strokeMiterlimit)));
|
||||
// TODO add dashing
|
||||
// CGPathCreateCopyByDashingPath(CGPathRef _Nullable path, const CGAffineTransform * _Nullable transform, CGFloat phase, const CGFloat * _Nullable lengths, size_t count)
|
||||
}
|
||||
@@ -460,9 +458,13 @@ UInt32 saturate(CGFloat value) {
|
||||
CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix);
|
||||
transformed = CGPointApplyAffineTransform(transformed, self.invTransform);
|
||||
|
||||
if (!CGRectContainsPoint(self.pathBounds, transformed)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
BOOL evenodd = self.fillRule == kRNSVGCGFCRuleEvenodd;
|
||||
if (!CGPathContainsPoint(_hitArea, nil, transformed, evenodd) &&
|
||||
!CGPathContainsPoint(_strokePath, nil, transformed, NO)) {
|
||||
!CGPathContainsPoint(self.strokePath, nil, transformed, NO)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ static CGFloat RNSVGTSpan_radToDeg = 180 / (CGFloat)M_PI;
|
||||
NSArray *lines;
|
||||
NSUInteger lineCount;
|
||||
BOOL isClosed;
|
||||
NSMutableArray *emoji;
|
||||
NSMutableArray *emojiTransform;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
@@ -33,6 +35,9 @@ static CGFloat RNSVGTSpan_radToDeg = 180 / (CGFloat)M_PI;
|
||||
RNSVGTSpan_separators = [NSCharacterSet whitespaceCharacterSet];
|
||||
}
|
||||
|
||||
emoji = [NSMutableArray arrayWithCapacity:0];
|
||||
emojiTransform = [NSMutableArray arrayWithCapacity:0];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -48,6 +53,21 @@ static CGFloat RNSVGTSpan_radToDeg = 180 / (CGFloat)M_PI;
|
||||
- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
|
||||
{
|
||||
if (self.content) {
|
||||
if (self.path) {
|
||||
NSUInteger count = [emoji count];
|
||||
RNSVGGlyphContext* gc = [self.textRoot getGlyphContext];
|
||||
CGFloat fontSize = [gc getFontSize];
|
||||
for (NSUInteger i = 0; i < count; i++) {
|
||||
UILabel *label = [emoji objectAtIndex:i];
|
||||
NSValue *transformValue = [emojiTransform objectAtIndex:i];
|
||||
CGAffineTransform transform = [transformValue CGAffineTransformValue];
|
||||
CGContextConcatCTM(context, transform);
|
||||
CGContextTranslateCTM(context, 0, -fontSize);
|
||||
[label.layer renderInContext:context];
|
||||
CGContextTranslateCTM(context, 0, fontSize);
|
||||
CGContextConcatCTM(context, CGAffineTransformInvert(transform));
|
||||
}
|
||||
}
|
||||
[self renderPathTo:context rect:rect];
|
||||
} else {
|
||||
[self clip:context];
|
||||
@@ -658,6 +678,9 @@ static CGFloat RNSVGTSpan_radToDeg = 180 / (CGFloat)M_PI;
|
||||
}
|
||||
}
|
||||
|
||||
[emoji removeAllObjects];
|
||||
[emojiTransform removeAllObjects];
|
||||
|
||||
CFArrayRef runs = CTLineGetGlyphRuns(line);
|
||||
CFIndex runEnd = CFArrayGetCount(runs);
|
||||
for (CFIndex r = 0; r < runEnd; r++) {
|
||||
@@ -829,6 +852,9 @@ static CGFloat RNSVGTSpan_radToDeg = 180 / (CGFloat)M_PI;
|
||||
[label.layer renderInContext:context];
|
||||
CGContextTranslateCTM(context, 0, fontSize);
|
||||
CGContextConcatCTM(context, CGAffineTransformInvert(transform));
|
||||
|
||||
[emoji addObject:label];
|
||||
[emojiTransform addObject:[NSValue valueWithCGAffineTransform:transform]];
|
||||
} else {
|
||||
transform = CGAffineTransformScale(transform, 1.0, -1.0);
|
||||
CGPathAddPath(path, &transform, glyphPath);
|
||||
|
||||
Reference in New Issue
Block a user