Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement maskUnits #2457

Merged
merged 5 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions android/src/main/java/com/horcrux/svg/MaskView.java
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
27 changes: 21 additions & 6 deletions android/src/main/java/com/horcrux/svg/RenderableView.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);

Expand Down
6 changes: 3 additions & 3 deletions apple/Filters/RNSVGFeOffset.mm
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,12 @@ - (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)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);

Expand Down
39 changes: 21 additions & 18 deletions apple/RNSVGRenderable.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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];

Expand Down Expand Up @@ -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();
Expand Down
9 changes: 7 additions & 2 deletions apple/Utils/RNSVGRenderUtils.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading