使用Quartz 2D / Core Graphics完成此绘图的最佳方法是什么?

时间:2022-08-10 23:37:17

As the background for one of the views in my app, I'd like to draw a fairly simple rectangular border just inside its frame. This would essentially be a rectangular gradient: a black line around the frame, fading to white about 10-20 pixels in. Unfortunately, as far as I can tell, Core Graphics doesn't provide rectangular gradients (either with CGGradient or CGShading). So I'm wondering what the best approach would be.

作为我的应用程序中的一个视图的背景,我想在其框架内绘制一个相当简单的矩形边框。这基本上是一个矩形渐变:框架周围的黑色线条,淡入白色大约10-20像素。不幸的是,据我所知,Core Graphics不提供矩形渐变(使用CGGradient或CGShading)。所以我想知道最好的方法是什么。

Two that occur to me:

发生在我身上的两件事:

  1. Draw a series of concentric rectangles, each subsequent one lighter in color, and inset by 1px on each side. I can't think of a simpler approach, but I have to do all of the gradient calculations myself, and it might be a lot of graphics operations.
  2. 绘制一系列同心矩形,每个随后一个颜色较浅,每边插入1px。我想不出更简单的方法,但我必须自己完成所有的梯度计算,并且它可能是很多图形操作。

  3. Use CGGradient in linear mode, once for each side. But for this to work, I think I'd need to set up a trapezoidal clipping area for each side first, so that the gradients would be mitered at the corners.
  4. 在线性模式下使用CGGradient,每侧一次。但为了实现这一点,我认为我需要首先为每一侧设置一个梯形剪裁区域,以便在角落处对渐变进行斜接。

Seems like there should be a way to use path stroking to do this, but it doesn't seem like there's a way to define a pattern that's oriented differently on each side.

似乎应该有一种方法来使用路径描边来做到这一点,但似乎没有办法定义一个在每一侧有不同方向的模式。

2 个解决方案

#1


I would go with option #2:

我会选择#2:

Use CGGradient in linear mode, once for each side. But for this to work, I think I'd need to set up a trapezoidal clipping area for each side first, so that the gradients would be mitered at the corners.

在线性模式下使用CGGradient,每侧一次。但为了实现这一点,我认为我需要首先为每一侧设置一个梯形剪裁区域,以便在角落处对渐变进行斜接。

Using NSBezierPath to create the trapezoidal regions would be fairly straightforward, and you would only have to perform four drawing operations.

使用NSBezierPath创建梯形区域将非常简单,您只需执行四次绘图操作。

Here's the basic code for creating the left side trapezoidal region:

这是创建左侧梯形区域的基本代码:

NSRect outer = [self bounds];
NSPoint outerPoint[4];
outerPoint[0] = NSMakePoint(0, 0);
outerPoint[1] = NSMakePoint(0, outer.size.height);
outerPoint[2] = NSMakePoint(outer.size.width, outer.size.height);
outerPoint[3] = NSMakePoint(outer.size.width, 0);

NSRect inner = NSInsetRect([self bounds], borderSize, borderSize);
NSPoint innerPoint[4];
innerPoint[0] = inner.origin;
innerPoint[1] = NSMakePoint(inner.origin.x,
                            inner.origin.y + inner.size.height);
innerPoint[2] = NSMakePoint(inner.origin.x + inner.size.width,
                            inner.origin.y + inner.size.height);
innerPoint[3] = NSMakePoint(inner.origin.x + inner.size.width,
                            inner.origin.y);

NSBezierPath leftSidePath = [[NSBezierPath bezierPath] retain];
[leftSidePath moveToPoint:outerPoint[0]];
[leftSidePath lineToPoint:outerPoint[1]];
[leftSidePath lineToPoint:innerPoint[1]];
[leftSidePath lineToPoint:innerPoint[0]];
[leftSidePath lineToPoint:outerPoint[0]];

// ... etc.

[leftSidePath release];

#2


something like this could also work. basically: instead of using clipping paths, simply use blendmode. and in this example the gradient is cached in a CGLayer.

像这样的东西也可以。基本上:不使用剪切路径,只需使用blendmode。在此示例中,渐变缓存在CGLayer中。

 CGContextRef ctx = UIGraphicsGetCurrentContext();
 CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB();

 CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 1.0);
 CGContextFillRect(ctx,self.bounds);

 CGFloat w = self.bounds.size.width;
 CGFloat h = self.bounds.size.height;
 CGFloat dh = (w-h)/2;

 CGLayerRef l = CGLayerCreateWithContext(ctx,CGSizeMake(h,48.0f),NULL);
 CGContextRef lctx = CGLayerGetContext(l);

 float comp[] = { .2,.5,1.0,1.0,1.0,1.0,1.0,1.0};
 CGGradientRef gradient = CGGradientCreateWithColorComponents(cspace, comp, NULL, 2);
 CGContextDrawLinearGradient(lctx, gradient,CGPointMake(0,0),CGPointMake(0,48), 0);

 CGContextSaveGState(ctx);
 CGContextSetBlendMode(ctx,kCGBlendModeDarken);
 for(int n=1;n<5;n++)
 {
  CGContextTranslateCTM(ctx,w/2.0,h/2.0);
  CGContextRotateCTM(ctx, M_PI_2);
  CGContextTranslateCTM(ctx,-w/2.0,-h/2.0);
  CGContextDrawLayerAtPoint(ctx,CGPointMake((n%2)*dh,(n%2)*-dh),l);
 }
 CGContextRestoreGState(ctx);

#1


I would go with option #2:

我会选择#2:

Use CGGradient in linear mode, once for each side. But for this to work, I think I'd need to set up a trapezoidal clipping area for each side first, so that the gradients would be mitered at the corners.

在线性模式下使用CGGradient,每侧一次。但为了实现这一点,我认为我需要首先为每一侧设置一个梯形剪裁区域,以便在角落处对渐变进行斜接。

Using NSBezierPath to create the trapezoidal regions would be fairly straightforward, and you would only have to perform four drawing operations.

使用NSBezierPath创建梯形区域将非常简单,您只需执行四次绘图操作。

Here's the basic code for creating the left side trapezoidal region:

这是创建左侧梯形区域的基本代码:

NSRect outer = [self bounds];
NSPoint outerPoint[4];
outerPoint[0] = NSMakePoint(0, 0);
outerPoint[1] = NSMakePoint(0, outer.size.height);
outerPoint[2] = NSMakePoint(outer.size.width, outer.size.height);
outerPoint[3] = NSMakePoint(outer.size.width, 0);

NSRect inner = NSInsetRect([self bounds], borderSize, borderSize);
NSPoint innerPoint[4];
innerPoint[0] = inner.origin;
innerPoint[1] = NSMakePoint(inner.origin.x,
                            inner.origin.y + inner.size.height);
innerPoint[2] = NSMakePoint(inner.origin.x + inner.size.width,
                            inner.origin.y + inner.size.height);
innerPoint[3] = NSMakePoint(inner.origin.x + inner.size.width,
                            inner.origin.y);

NSBezierPath leftSidePath = [[NSBezierPath bezierPath] retain];
[leftSidePath moveToPoint:outerPoint[0]];
[leftSidePath lineToPoint:outerPoint[1]];
[leftSidePath lineToPoint:innerPoint[1]];
[leftSidePath lineToPoint:innerPoint[0]];
[leftSidePath lineToPoint:outerPoint[0]];

// ... etc.

[leftSidePath release];

#2


something like this could also work. basically: instead of using clipping paths, simply use blendmode. and in this example the gradient is cached in a CGLayer.

像这样的东西也可以。基本上:不使用剪切路径,只需使用blendmode。在此示例中,渐变缓存在CGLayer中。

 CGContextRef ctx = UIGraphicsGetCurrentContext();
 CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB();

 CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 1.0);
 CGContextFillRect(ctx,self.bounds);

 CGFloat w = self.bounds.size.width;
 CGFloat h = self.bounds.size.height;
 CGFloat dh = (w-h)/2;

 CGLayerRef l = CGLayerCreateWithContext(ctx,CGSizeMake(h,48.0f),NULL);
 CGContextRef lctx = CGLayerGetContext(l);

 float comp[] = { .2,.5,1.0,1.0,1.0,1.0,1.0,1.0};
 CGGradientRef gradient = CGGradientCreateWithColorComponents(cspace, comp, NULL, 2);
 CGContextDrawLinearGradient(lctx, gradient,CGPointMake(0,0),CGPointMake(0,48), 0);

 CGContextSaveGState(ctx);
 CGContextSetBlendMode(ctx,kCGBlendModeDarken);
 for(int n=1;n<5;n++)
 {
  CGContextTranslateCTM(ctx,w/2.0,h/2.0);
  CGContextRotateCTM(ctx, M_PI_2);
  CGContextTranslateCTM(ctx,-w/2.0,-h/2.0);
  CGContextDrawLayerAtPoint(ctx,CGPointMake((n%2)*dh,(n%2)*-dh),l);
 }
 CGContextRestoreGState(ctx);