diff --git a/android/src/main/java/com/horcrux/svg/MaskView.java b/android/src/main/java/com/horcrux/svg/MaskView.java index 6dc4fc023..695e4ef3f 100644 --- a/android/src/main/java/com/horcrux/svg/MaskView.java +++ b/android/src/main/java/com/horcrux/svg/MaskView.java @@ -58,6 +58,10 @@ public void setHeight(Dynamic height) { invalidate(); } + public Brush.BrushUnits getMaskUnits() { + return mMaskUnits; + } + public void setMaskUnits(int maskUnits) { switch (maskUnits) { case 0: diff --git a/android/src/main/java/com/horcrux/svg/RenderableView.java b/android/src/main/java/com/horcrux/svg/RenderableView.java index 990b7a2f4..b24e0a184 100644 --- a/android/src/main/java/com/horcrux/svg/RenderableView.java +++ b/android/src/main/java/com/horcrux/svg/RenderableView.java @@ -411,12 +411,27 @@ void render(Canvas canvas, Paint paint, float opacity) { } // calculate mask bounds - float maskX = (float) relativeOnWidth(mask.mX); - float maskY = (float) relativeOnHeight(mask.mY); - float maskWidth = (float) relativeOnWidth(mask.mW); - float maskHeight = (float) relativeOnHeight(mask.mH); + RectF maskBounds; + if (mask.getMaskUnits() == Brush.BrushUnits.USER_SPACE_ON_USE) { + float maskX = (float) relativeOnWidth(mask.mX); + float maskY = (float) relativeOnHeight(mask.mY); + float maskWidth = (float) relativeOnWidth(mask.mW); + float maskHeight = (float) relativeOnHeight(mask.mH); + maskBounds = new RectF(maskX, maskY, maskX + maskWidth, maskY + maskHeight); + } else { // Brush.BrushUnits.OBJECT_BOUNDING_BOX + RectF clientRect = this.getClientRect(); + if (this instanceof ImageView && clientRect == null) { + return; + } + mInvCTM.mapRect(clientRect); + float maskX = (float) relativeOnFraction(mask.mX, clientRect.width()); + float maskY = (float) relativeOnFraction(mask.mY, clientRect.height()); + float maskWidth = (float) relativeOnFraction(mask.mW, clientRect.width()); + float maskHeight = (float) relativeOnFraction(mask.mH, clientRect.height()); + maskBounds = new RectF(clientRect.left + maskX, clientRect.top + maskY, clientRect.left + maskX + maskWidth, clientRect.top + maskY + maskHeight); + } // clip to mask bounds - canvas.clipRect(maskX, maskY, maskX + maskWidth, maskY + maskHeight); + canvas.clipRect(maskBounds); mask.draw(canvas, paint, 1f); @@ -426,7 +441,7 @@ void render(Canvas canvas, Paint paint, float opacity) { // step 2 - alpha layer canvas.saveLayer(null, dstInPaint); // clip to mask bounds - canvas.clipRect(maskX, maskY, maskX + maskWidth, maskY + maskHeight); + canvas.clipRect(maskBounds); mask.draw(canvas, paint, 1f); diff --git a/apple/Filters/RNSVGFeOffset.mm b/apple/Filters/RNSVGFeOffset.mm index c733fe81d..ca236e043 100644 --- a/apple/Filters/RNSVGFeOffset.mm +++ b/apple/Filters/RNSVGFeOffset.mm @@ -117,12 +117,12 @@ - (CIImage *)applyFilter:(NSMutableDictionary *)results CGAffineTransform contextTransform = CGAffineTransformConcat(ctm, CGAffineTransformMakeTranslation(-ctm.tx, -ctm.ty)); #if !TARGET_OS_OSX // [macOS] CGPoint translate = CGPointMake(dx, dy); -#else - CGPoint translate = CGPointMake(dx, -dy); +#else // [macOS + CGPoint translate = CGPointMake(dx, dy); CGFloat scale = [RNSVGRenderUtils getScreenScale]; CGAffineTransform screenScaleCTM = CGAffineTransformMake(scale, 0, 0, scale, 0, 0); translate = CGPointApplyAffineTransform(translate, screenScaleCTM); -#endif +#endif // macOS] translate = CGPointApplyAffineTransform(translate, contextTransform); CGAffineTransform transform = CGAffineTransformMakeTranslation(translate.x, translate.y); diff --git a/apple/RNSVGRenderable.mm b/apple/RNSVGRenderable.mm index cefc10ff7..5c72547c1 100644 --- a/apple/RNSVGRenderable.mm +++ b/apple/RNSVGRenderable.mm @@ -270,7 +270,11 @@ - (void)renderTo:(CGContextRef)context rect:(CGRect)rect CGAffineTransform screenScaleCTM = CGAffineTransformMake(scale, 0, 0, scale, 0, 0); CGRect scaledRect = CGRectApplyAffineTransform(rect, screenScaleCTM); +#if TARGET_OS_OSX + CGImage *contentImage = [RNSVGRenderUtils renderToImage:self ctm:currentCTM rect:scaledRect clip:nil]; +#else CGImage *contentImage = [RNSVGRenderUtils renderToImage:self ctm:currentCTM rect:rect clip:nil]; +#endif if (filterNode) { // https://www.w3.org/TR/SVG11/filters.html#FilterElement @@ -292,12 +296,7 @@ - (void)renderTo:(CGContextRef)context rect:(CGRect)rect if (!maskNode) { CGContextConcatCTM(context, CGAffineTransformInvert(currentCTM)); - - // On macOS the currentCTM is inverted, so we need to transform it again - // https://stackoverflow.com/a/13358085 #if TARGET_OS_OSX - CGContextTranslateCTM(context, 0.0, rect.size.height); - CGContextScaleCTM(context, 1.0, -1.0); CGContextDrawImage(context, rect, contentImage); #else CGContextDrawImage(context, scaledRect, contentImage); @@ -328,18 +327,24 @@ - (void)renderTo:(CGContextRef)context rect:(CGRect)rect #if TARGET_OS_OSX // [macOS] // on macOS currentCTM is not scaled properly with screen scale so we need to scale it manually CGContextConcatCTM(bcontext, screenScaleCTM); - CGContextTranslateCTM(bcontext, 0, rect.size.height); - CGContextScaleCTM(bcontext, 1, -1); - #endif // [macOS] CGContextConcatCTM(bcontext, currentCTM); // Clip to mask bounds and render the mask - CGSize currentBoundsSize = self.pathBounds.size; - CGFloat x = [self relativeOnFraction:[maskNode x] relative:currentBoundsSize.width]; - CGFloat y = [self relativeOnFraction:[maskNode y] relative:currentBoundsSize.height]; - CGFloat w = [self relativeOnFraction:[maskNode maskwidth] relative:currentBoundsSize.width]; - CGFloat h = [self relativeOnFraction:[maskNode maskheight] relative:currentBoundsSize.height]; - CGRect maskBounds = CGRectApplyAffineTransform(CGRectMake(x, y, w, h), screenScaleCTM); + CGRect maskBounds; + if ([maskNode maskUnits] == RNSVGUnits::kRNSVGUnitsUserSpaceOnUse) { + CGFloat x = [self relativeOn:[maskNode x] relative:width]; + CGFloat y = [self relativeOn:[maskNode y] relative:height]; + CGFloat w = [self relativeOn:[maskNode maskwidth] relative:width]; + CGFloat h = [self relativeOn:[maskNode maskheight] relative:height]; + maskBounds = CGRectMake(x, y, w, h); + } else { + CGSize currentBoundsSize = self.pathBounds.size; + CGFloat x = [self relativeOnFraction:[maskNode x] relative:currentBoundsSize.width]; + CGFloat y = [self relativeOnFraction:[maskNode y] relative:currentBoundsSize.height]; + CGFloat w = [self relativeOnFraction:[maskNode maskwidth] relative:currentBoundsSize.width]; + CGFloat h = [self relativeOnFraction:[maskNode maskheight] relative:currentBoundsSize.height]; + maskBounds = CGRectMake(self.pathBounds.origin.x + x, self.pathBounds.origin.y + y, w, h); + } CGContextClipToRect(bcontext, maskBounds); [maskNode renderLayerTo:bcontext rect:bounds]; @@ -394,12 +399,10 @@ - (void)renderTo:(CGContextRef)context rect:(CGRect)rect UIGraphicsBeginImageContextWithOptions(rect.size, NO, scale); CGContextRef newContext = UIGraphicsGetCurrentContext(); - CGContextConcatCTM(newContext, CGAffineTransformInvert(CGContextGetCTM(newContext))); - CGContextSetBlendMode(newContext, kCGBlendModeCopy); - CGContextDrawImage(newContext, scaledRect, maskImage); + CGContextDrawImage(newContext, rect, maskImage); CGContextSetBlendMode(newContext, kCGBlendModeSourceIn); - CGContextDrawImage(newContext, scaledRect, contentImage); + CGContextDrawImage(newContext, rect, contentImage); CGImageRef blendedImage = CGBitmapContextCreateImage(newContext); UIGraphicsEndImageContext(); diff --git a/apple/Utils/RNSVGRenderUtils.mm b/apple/Utils/RNSVGRenderUtils.mm index 597195476..a501fbd7f 100644 --- a/apple/Utils/RNSVGRenderUtils.mm +++ b/apple/Utils/RNSVGRenderUtils.mm @@ -36,11 +36,16 @@ + (CGImage *)renderToImage:(RNSVGRenderable *)renderable clip:(CGRect *)clip { CGFloat scale = [self getScreenScale]; +#if TARGET_OS_OSX // [macOS + UIGraphicsBeginImageContextWithOptions(rect.size, NO, 1.0); +#else // macOS] UIGraphicsBeginImageContextWithOptions(rect.size, NO, scale); +#endif // [macOS] CGContextRef cgContext = UIGraphicsGetCurrentContext(); -#if !TARGET_OS_OSX CGContextConcatCTM(cgContext, CGAffineTransformInvert(CGContextGetCTM(cgContext))); -#endif +#if TARGET_OS_OSX // [macOS + CGContextConcatCTM(cgContext, CGAffineTransformMakeScale(scale, scale)); +#endif // macOS] CGContextConcatCTM(cgContext, ctm); if (clip) {