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

NSBezierPath does not clip gradients #228

Open
optimisme opened this issue Dec 23, 2023 · 6 comments
Open

NSBezierPath does not clip gradients #228

optimisme opened this issue Dec 23, 2023 · 6 comments

Comments

@optimisme
Copy link

NSBeizerPath does not clip gradients properly, see

                NSColor *startColor = GVThemeColorRGB(35, 135, 255, 1.0);
                NSColor *endColor = GVThemeColorRGB(0, 110, 255, 1.0);
                NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:startColor endingColor:endColor];

                NSBezierPath    *bezelPath2 = [NSBezierPath bezierPathWithRoundedRect:paddedFrame xRadius:15.0 yRadius:15.0];
                [NSGraphicsContext saveGraphicsState];
                [bezelPath2 setClip];
                [gradient drawInRect:frame angle:90.0]; 
                [NSGraphicsContext restoreGraphicsState];

                [[NSColor redColor] setStroke];
                [bezelPath2 setLineWidth:1.0];
                [bezelPath2 stroke];

The exepected behaviour is seeing nothing blue out of red circle

Screenshot 2023-12-23 at 01 15 23
@fredkiefer fredkiefer changed the title NSBeizerPath does not clip gradients NSBezierPath does not clip gradients Dec 23, 2023
@fredkiefer
Copy link
Member

This bug report is rather disturbing. Up to now I had the impression that clipping was more or less working and there is no special magic build in for gradients. It should just respect the normal clipping mechanisms.
Which backend are you using? At the moment cairo is the best supported backend, you really should be working with that.

@optimisme
Copy link
Author

I installed GNUStep on Ubuntu 22.04 using this repo
https://github.com/plaurent/gnustep-build
For me it is the easiest way to install and updated version of GNUStep
Looking at the install script:
https://github.com/plaurent/gnustep-build/blob/master/ubuntu-22.04-clang-14.0-runtime-2.1/GNUstep-buildon-ubuntu2204.sh
I think my installation is using Cairo backend.

@optimisme
Copy link
Author

I attach two images from the next test source.

Top image is rendered by GNUStep:

  • First poligon is not clipping the gradient
  • The star looks right
  • The Oval did not apply the gradient right
- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    // Clipped gradient example
    NSColor *startColor = [NSColor greenColor];
    NSColor *endColor = [NSColor blueColor];
    NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:startColor endingColor:endColor];

    NSRect frame = NSMakeRect(50, 50, 100, 50);
    NSBezierPath *bezelPath = [NSBezierPath bezierPathWithRoundedRect:frame xRadius:15.0 yRadius:15.0];
    [NSGraphicsContext saveGraphicsState];
    [bezelPath setClip];
    [gradient drawInRect:frame angle:90.0];
    [NSGraphicsContext restoreGraphicsState];

    // Star
    NSColor *starStartColor = [NSColor redColor];
    NSColor *starEndColor = [NSColor yellowColor];
    NSGradient *starGradient = [[NSGradient alloc] initWithStartingColor:starStartColor endingColor:starEndColor];

    NSBezierPath *starPath = [NSBezierPath bezierPath];
    CGFloat starRadius = 50.0;  // Radi exterior de la estrella
    CGFloat innerRadius = starRadius * sin(M_PI / 10) / sin(7 * M_PI / 10); // Radi interior
    NSPoint center = NSMakePoint(250, 100); // Centre de l'estrella
    for (int i = 0; i < 10; i++) {
        // Calculem l'angle per a cada punt
        CGFloat angle = (CGFloat)(2 * M_PI / 10 * i);

        // Alternem entre radi exterior i interior
        CGFloat radius = i % 2 == 0 ? starRadius : innerRadius;
        CGFloat x = center.x + sin(angle) * radius;
        CGFloat y = center.y + cos(angle) * radius;

        if (i == 0) {
            [starPath moveToPoint:NSMakePoint(x, y)];
        } else {
            [starPath lineToPoint:NSMakePoint(x, y)];
        }
    }
    [starPath closePath];

    [NSGraphicsContext saveGraphicsState];
    [starPath setClip];
    [starGradient drawInBezierPath:starPath angle:90.0];
    [NSGraphicsContext restoreGraphicsState];
    
    // 45 degrees Oval
    NSRect ovalRect = NSMakeRect(350, 50, 100, 50);
    NSBezierPath *ovalPath = [NSBezierPath bezierPathWithOvalInRect:ovalRect];
    [NSGraphicsContext saveGraphicsState];
    NSAffineTransform *transform = [NSAffineTransform transform];
    NSPoint centerOval = NSMakePoint(NSMidX(ovalRect), NSMidY(ovalRect));
    [transform translateXBy:centerOval.x yBy:centerOval.y];
    [transform rotateByDegrees:45];
    [transform translateXBy:-centerOval.x yBy:-centerOval.y];
    [ovalPath transformUsingAffineTransform:transform];
    [ovalPath setClip];
    [NSGraphicsContext restoreGraphicsState];
    [gradient drawInBezierPath:ovalPath angle:-25.0];   
}
Captura de pantalla 2023-12-25 a les 17 59 18

@optimisme
Copy link
Author

This is the project with the example
test-bug-gradients.zip

@fredkiefer
Copy link
Member

Thank you very much for these examples. The first looks like a bug in GNUstep and in the new year I hope to find time to investigate that. As for the third example I don't understand how this is working on macOS. What is the setClip doing here? I really need to spend more time on that one.

@fredkiefer
Copy link
Member

I looked into the first issue and it is even worse. This is a limitation of the way we interact with the cairo library. When saving the graphics state we need to copy the state within cairo and there is a limitation that only rectangular clipping can be copied correctly. For all other cases a replacement gets used. In this case a rectangular one, which looks like no clipping at all. Maybe it would be possible to implement this interaction completely different, but although I wrote most of the original one, I don't see how to do it differently. This may be the reason for quite a few of the drawing artefacts you may see. As this happens every time a non rectangular shape gets used.

I still need to look into the third case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants