Open optimisme opened 9 months ago
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.
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.
I attach two images from the next test source.
Top image is rendered by GNUStep:
- (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];
}
This is the project with the example test-bug-gradients.zip
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.
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.
NSBeizerPath does not clip gradients properly, see
The exepected behaviour is seeing nothing blue out of red circle