From 5551ff2bfa737310f22a689da5d91edbcd92d000 Mon Sep 17 00:00:00 2001 From: Mikael Sand Date: Fri, 25 Jan 2019 04:23:33 +0200 Subject: [PATCH] Fix #840 Implement support for patternUnits="objectBoundingBox" seems to work well on both platforms Implement support for patternContentUnits="objectBoundingBox" Android has issues with small paths --- .../src/main/java/com/horcrux/svg/Brush.java | 25 +++++++++--- elements/Pattern.js | 7 ++++ ios/Brushes/RNSVGPainter.h | 2 + ios/Brushes/RNSVGPainter.m | 38 +++++++++++-------- ios/Elements/RNSVGGroup.m | 4 +- 5 files changed, 52 insertions(+), 24 deletions(-) diff --git a/android/src/main/java/com/horcrux/svg/Brush.java b/android/src/main/java/com/horcrux/svg/Brush.java index 699f8522..dc3ca2dc 100644 --- a/android/src/main/java/com/horcrux/svg/Brush.java +++ b/android/src/main/java/com/horcrux/svg/Brush.java @@ -33,7 +33,7 @@ class Brush { // TODO implement pattern units @SuppressWarnings({"FieldCanBeLocal", "unused"}) - private boolean mUseContentObjectBoundingBox; + private boolean mUseContentObjectBoundingBoxUnits; private Matrix mMatrix; private Rect mUserSpaceBoundingBox; @@ -46,7 +46,7 @@ class Brush { } void setContentUnits(BrushUnits units) { - mUseContentObjectBoundingBox = units == BrushUnits.OBJECT_BOUNDING_BOX; + mUseContentObjectBoundingBoxUnits = units == BrushUnits.OBJECT_BOUNDING_BOX; } void setPattern(PatternView pattern) { @@ -104,6 +104,11 @@ class Brush { return new RectF(x, y, x + width, y + height); } + private double getVal(SVGLength length, double relative, float scale, float textSize) { + return PropHelper.fromRelative(length, relative, 0, mUseObjectBoundingBox && + length.unit == SVGLengthUnitType.SVG_LENGTHTYPE_NUMBER ? relative : scale, textSize); + } + void setupPaint(Paint paint, RectF pathBoundingBox, float scale, float opacity) { RectF rect = getPaintRect(pathBoundingBox); float width = rect.width(); @@ -113,10 +118,14 @@ class Brush { float textSize = paint.getTextSize(); if (mType == BrushType.PATTERN) { - double x = PropHelper.fromRelative(mPoints[0], width, offsetX, scale, textSize); - double y = PropHelper.fromRelative(mPoints[1], height, offsetY, scale, textSize); - double w = PropHelper.fromRelative(mPoints[2], width, offsetX, scale, textSize); - double h = PropHelper.fromRelative(mPoints[3], height, offsetY, scale, textSize); + double x = getVal(mPoints[0], width, scale, textSize); + double y = getVal(mPoints[1], height, scale, textSize); + double w = getVal(mPoints[2], width, scale, textSize); + double h = getVal(mPoints[3], height, scale, textSize); + + if (!(w > 1 && h > 1)) { + return; + } Bitmap bitmap = Bitmap.createBitmap( (int) w, @@ -131,6 +140,10 @@ class Brush { canvas.concat(mViewBoxMatrix); } + if (mUseContentObjectBoundingBoxUnits) { + canvas.scale(width / scale, height / scale); + } + mPattern.draw(canvas, new Paint(), opacity); Matrix patternMatrix = new Matrix(); diff --git a/elements/Pattern.js b/elements/Pattern.js index a76084ed..2f2d6199 100644 --- a/elements/Pattern.js +++ b/elements/Pattern.js @@ -8,6 +8,13 @@ import Shape from "./Shape"; export default class Pattern extends Shape { static displayName = "Pattern"; + static defaultProps = { + x: "0%", + y: "0%", + width: "100%", + height: "100%", + }; + render() { const { props } = this; const { diff --git a/ios/Brushes/RNSVGPainter.h b/ios/Brushes/RNSVGPainter.h index d0b4f28c..f7e73853 100644 --- a/ios/Brushes/RNSVGPainter.h +++ b/ios/Brushes/RNSVGPainter.h @@ -17,6 +17,8 @@ @property (nonatomic, assign) RNSVGPattern* pattern; @property (nonatomic, assign) CGRect paintBounds; +@property (nonatomic, assign) bool useObjectBoundingBoxForContentUnits; +@property (nonatomic, assign) CGRect bounds; - (instancetype)initWithPointsArray:(NSArray *)pointsArray NS_DESIGNATED_INITIALIZER; diff --git a/ios/Brushes/RNSVGPainter.m b/ios/Brushes/RNSVGPainter.m index 7c977ef7..b7361681 100644 --- a/ios/Brushes/RNSVGPainter.m +++ b/ios/Brushes/RNSVGPainter.m @@ -131,34 +131,40 @@ void PatternFunction(void* info, CGContextRef context) CGContextConcatCTM(context, _viewBoxTransform); } + if (_painter.useObjectBoundingBoxForContentUnits) { + CGRect bounds = _painter.bounds; + CGContextConcatCTM(context, CGAffineTransformMakeScale(bounds.size.width, bounds.size.height)); + } + [_pattern renderTo:context rect:rect]; } +- (CGFloat)getVal:(RNSVGLength*)length relative:(CGFloat)relative +{ + RNSVGLengthUnitType unit = [length unit]; + CGFloat val = [RNSVGPropHelper fromRelative:length + relative:relative]; + return _useObjectBoundingBox && + unit == SVG_LENGTHTYPE_NUMBER ? val * relative : val; +} + - (void)paintPattern:(CGContextRef)context bounds:(CGRect)bounds { CGRect rect = [self getPaintRect:context bounds:bounds]; CGFloat height = CGRectGetHeight(rect); CGFloat width = CGRectGetWidth(rect); - CGFloat offsetX = CGRectGetMinX(rect); - CGFloat offsetY = CGRectGetMinY(rect); - CGFloat x = [RNSVGPercentageConverter lengthToFloat:[_points objectAtIndex:0] - relative:width - offset:offsetX]; - CGFloat y = [RNSVGPercentageConverter lengthToFloat:[_points objectAtIndex:1] - relative:height - offset:offsetY]; - CGFloat w = [RNSVGPercentageConverter lengthToFloat:[_points objectAtIndex:2] - relative:width - offset:offsetX]; - CGFloat h = [RNSVGPercentageConverter lengthToFloat:[_points objectAtIndex:3] - relative:height - offset:offsetY]; + CGFloat x = [self getVal:[_points objectAtIndex:0] relative:width]; + CGFloat y = [self getVal:[_points objectAtIndex:1] relative:height]; + CGFloat w = [self getVal:[_points objectAtIndex:2] relative:width]; + CGFloat h = [self getVal:[_points objectAtIndex:3] relative:height]; CGAffineTransform viewbox = [self.pattern.svgView getViewBoxTransform]; - CGRect newBounds = CGRectApplyAffineTransform(CGRectMake(x, y, w, h), viewbox); + CGRect newBounds = CGRectMake(x, y, w, h); CGSize size = newBounds.size; + self.useObjectBoundingBoxForContentUnits = _useContentObjectBoundingBox; self.paintBounds = newBounds; + self.bounds = rect; const CGPatternCallbacks callbacks = { 0, &PatternFunction, NULL }; CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL); @@ -167,7 +173,7 @@ void PatternFunction(void* info, CGContextRef context) CGPatternRef pattern = CGPatternCreate((__bridge void * _Nullable)(self), newBounds, - CGAffineTransformIdentity, + viewbox, size.width, size.height, kCGPatternTilingConstantSpacing, diff --git a/ios/Elements/RNSVGGroup.m b/ios/Elements/RNSVGGroup.m index 189b67be..b9c3ed10 100644 --- a/ios/Elements/RNSVGGroup.m +++ b/ios/Elements/RNSVGGroup.m @@ -133,7 +133,7 @@ } CGMutablePathRef __block path = CGPathCreateMutable(); [self traverseSubviews:^(RNSVGNode *node) { - if ([node isKindOfClass:[RNSVGNode class]]) { + if ([node isKindOfClass:[RNSVGNode class]] && ![node isKindOfClass:[RNSVGMask class]]) { CGAffineTransform transform = CGAffineTransformConcat(node.matrix, node.transforms); CGPathAddPath(path, &transform, [node getPath:context]); } @@ -174,7 +174,7 @@ } for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) { - if (![node isKindOfClass:[RNSVGNode class]]) { + if (![node isKindOfClass:[RNSVGNode class]] || [node isKindOfClass:[RNSVGMask class]]) { continue; }