iphone:将纹理应用到 CGContextStrokePath

Posted

技术标签:

【中文标题】iphone:将纹理应用到 CGContextStrokePath【英文标题】:iphone: Apply a texture to CGContextStrokePath 【发布时间】:2011-07-09 14:22:53 【问题描述】:

我在 *** 中搜索了所有问题和答案,但无法得到我想要的结果。我想使用 PNG 文件,在用户触摸和移动时显示为笔画。这是PNG文件:

但是我在 *** 上做了所有的事情,甚至使用了 CGPatternRef,但我没有得到我想要的结果。我得到的结果在这里:

但我想要的结果在这里:

我已经在 OpenGL ES 中完成了这个结果,但在 Core Graphics 中我无法做到。我想我可以在每个点使用 CGContextDrawImage,但这种方式会导致性能不佳,因为我必须在用户触摸的 2 个点之间绘制所有点的图像。我想你以前遇到过这个问题。你认为我可以在 Core Graphics 中应用纹理来绘制这样的笔划吗?非常感谢。

【问题讨论】:

海军士!你有没有解决过这个问题?花了很长时间才回答:) 【参考方案1】:

当您使用图案创建线条时,它将看起来像第一个图像,而不是第二个图像。线条只是您提供的纹理图像的平铺,裁剪成线条的形状。它无法知道您打算将圆圈合并在一起以看起来像一条实线。为此,您可能应该只用纯黑色画一条线。

对于任何试图在第一张图片中实现图案线的人,这里有一个很好的教程来解释如何做到这一点:http://blog.robertturrall.com/tag/cgcontextsetstrokepattern/

基本上你需要的是:

// first define the pattern width and height
const float kPatternWidth = 8;
const float kPatternHeight = 8;

void DrawPatternCellCallback(void *info, CGContextRef cgContext) 

    // Create a CGImage and use CGContextDrawImage() to draw it into the graphics context provided by the callback function.
    UIImage *patternImage = [UIImage imageNamed:@"yourtextureimage.png"];
    CGContextDrawImage(cgContext, CGRectMake(0, 0, kPatternWidth, kPatternHeight), patternImage.CGImage);

确保“void”周围没有括号。还要确保 DrawPatternCellCallback 部分出现在 drawRect 之前,否则它将不起作用。

那么你的 drawRect 需要以下内容:

- (void)drawRect:(CGRect)rect 
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    const CGRect patternBounds = CGRectMake(0, 0, kPatternWidth, kPatternHeight);
    const CGPatternCallbacks kPatternCallbacks = 0, DrawPatternCellCallback, NULL;

    CGAffineTransform patternTransform = CGAffineTransformIdentity;
    CGPatternRef strokePattern = CGPatternCreate(
                                             NULL,
                                             patternBounds,
                                             patternTransform,
                                             kPatternWidth, // horizontal spacing
                                             kPatternHeight, // vertical spacing
                                             kCGPatternTilingNoDistortion,
                                             true,
                                             &kPatternCallbacks);
    CGFloat color1[] = 1.0, 1.0, 1.0, 1.0;

    CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
    CGContextSetStrokeColorSpace(ctx, patternSpace);
    CGContextSetStrokePattern(ctx, strokePattern, color1);
    CGContextSetLineWidth(ctx, 4.0);
    CGContextDrawPath(ctx);

    // If you aren't using ARC:
    CGPatternRelease(strokePattern);
    strokePattern = NULL;
    CGColorSpaceRelease(patternSpace);
    patternSpace = NULL;

【讨论】:

我很确定你需要 "CGPatternRelease(strokePattern);"即使您使用的是 ARC。它仅适用于 objetive-c 对象,而不适用于 CoreGraphics 之类的直接 C 代码。 我相信这是“单例”示例代码:这是整个互联网上唯一一个简单的图案绘制示例代码 :) 干得好,恭喜...【参考方案2】:

关键 ...

例如,如果您经常在自身上绘制图像的模糊版本。

您几乎肯定会需要这样的代码...

float isf;
isf = [[UIScreen mainScreen] bounds].size.width / wholeImageWidth;

(如果您正在全屏绘制;如果在窗口中绘制,则以明显的方式进行调整)其中 wholeImageWidth 是您正在编辑的图像的实际宽度。

然后当您创建笔画图案时,您会像这样使用它

CGAffineTransformScale( CGAffineTransformIdentity, isf, -isf)

但请注意,令人讨厌的是,您可能必须为相机和相册以不同的方式重新旋转它。所以像.......

if ( self.wasFromCameraNotAlbum )
  
  strokePattern = CGPatternCreate(
     NULL,
     CGRectMake(0,0,wholeWidth,wholeHeight),

     CGAffineTransformConcat(
         CGAffineTransformScale( CGAffineTransformIdentity, isf, -isf),
         CGAffineTransformRotate( CGAffineTransformIdentity, 90*M_PI/180 )
         ),

     wholeWidth,wholeHeight,
     kCGPatternTilingNoDistortion, true, &kPatternCallbacks);
  
else
  
  strokePattern = CGPatternCreate(
     NULL,
     CGRectMake(0,0,wholeWidth,wholeHeight),

     CGAffineTransformScale( CGAffineTransformIdentity, isf, -isf),

     wholeWidth,wholeHeight,
     kCGPatternTilingNoDistortion, true, &kPatternCallbacks);
  

对于谷歌搜索,这是由于相机、旋转、iPhone 6+、ios8、相册。


既然 llama591 的回答太棒了。这是一些示例代码,可能有一天会帮助某人。这是我可以为图案绘制编写的最简单的代码

这在 Xcode5/iOS7(2014 年 1 月)上运行良好:

这是一些非常典型的绘制蓝线的绘图代码:

-(void)drawRect:(CGRect)rect

[curImage drawAtPoint:CGPointMake(0, 0)];
CGPoint mid1 = midPoint(previousPoint1, previousPoint2); 
CGPoint mid2 = midPoint(currentPoint, previousPoint1);
CGContextRef context = UIGraphicsGetCurrentContext(); 
[self.layer renderInContext:context];
CGContextMoveToPoint(context, mid1.x, mid1.y);
CGContextAddQuadCurveToPoint(context,
  previousPoint1.x, previousPoint1.y, mid2.x, mid2.y); 
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, 20.0);

CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);

CGContextStrokePath(context);            
[super drawRect:rect];

注意调用 CGContextSetStrokeColorWithColor,它只是将线设置为实线,在本例中为蓝色。

接下来是相同的代码。但是,我们将那一行代码替换为所有用于绘制图案的代​​码。 (而且还有一个新的单独的回调例程,很简单。)

为了清楚起见,这里只有 15 行代码,它将替换单个简单的 StrokeColor 代码行:

const CGPatternCallbacks kPatternCallbacks = 0, simplePatternCallback, NULL;
CGFloat color1[] = 1.0, 1.0, 1.0, 1.0;
CGPatternRef strokePattern = CGPatternCreate(
    NULL,
    CGRectMake(0,0,100,100),
    CGAffineTransformIdentity,
    100,100,
    kCGPatternTilingNoDistortion,
    true,
    &kPatternCallbacks);
CGContextSetStrokeColorSpace(UIGraphicsGetCurrentContext(),
        CGColorSpaceCreatePattern(NULL););
CGContextSetStrokePattern(context, strokePattern, color1);
CGPatternRelease(strokePattern);
strokePattern = NULL;

您的项目中的图案应该是 PNG,可以说是 100x100 像素,这里称为“pattern100100.png”。

再次,我将 1 行代码替换为该模式的 15 行代码。 (还有一个单独的微不足道的回调函数。)这是整个事情:

-(void)drawRect:(CGRect)rect

[curImage drawAtPoint:CGPointMake(0, 0)];
CGPoint mid1 = midPoint(previousPoint1, previousPoint2); 
CGPoint mid2 = midPoint(currentPoint, previousPoint1);
CGContextRef context = UIGraphicsGetCurrentContext(); 
[self.layer renderInContext:context];
CGContextMoveToPoint(context, mid1.x, mid1.y);
CGContextAddQuadCurveToPoint(context,
  previousPoint1.x, previousPoint1.y, mid2.x, mid2.y); 
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, 20.0);

const CGPatternCallbacks kPatternCallbacks = 0, simplePatternCallback, NULL;
CGFloat color1[] = 1.0, 1.0, 1.0, 1.0;
CGPatternRef strokePattern = CGPatternCreate(
    NULL,
    CGRectMake(0,0,100,100),
    CGAffineTransformIdentity,
    100,100,
    kCGPatternTilingNoDistortion,
    true,
    &kPatternCallbacks);
CGContextSetStrokeColorSpace(UIGraphicsGetCurrentContext(),
        CGColorSpaceCreatePattern(NULL););
CGContextSetStrokePattern(context, strokePattern, color1);
CGPatternRelease(strokePattern);
strokePattern = NULL;

CGContextStrokePath(context);            
[super drawRect:rect];


void simplePatternCallback(void *info, CGContextRef ctx)

UIImage *brushTexture = [UIImage imageNamed:@"pattern100100.png"];
CGContextDrawImage(ctx, CGRectMake(0, 0, 100,100), brushTexture.CGImage);

在代码中包含几个有用的 cmets!

// re the final argument:
// from Apple doco: "If the specified pattern is a 'colored' pattern,
// pass an alpha value."
// in fact you have to pass in an array of four values.

// note, "stencil patterns" (aka "uncolored patterns") patterns
// are basically masks, rarely used

// re the "true" in CGPatternCreate, that means "coloured pattern"
// (ie not "stencil pattern")

我真的希望它对某人有所帮助。

注意:每当您看到这样的代码示例时(即使在 Apple)。他们总是“在 drawRect 内”做所有的事情。请注意,每次调用 drawRect 时都会创建“strokePattern”!对我来说,这似乎很疯狂,我总是简单地将变量或属性用于“strokePattern”和其他元素。同样,在回调中每次都使用 imageNamed 会很荒谬,只需使用您之前准备的属性即可。

再次,我真的希望它对某人有所帮助。干杯


有用的脚注: 在 CGPaternCreate 中,示例代码显示了参数三的 CGAffineTransformIdentity。

通常,您会使用一些照片或其他动态图像。在那种情况下,几乎可以肯定会是......

CGAffineTransformScale(CGAffineTransformIdentity,1,-1),

处理核心图形中的倒置比例。希望对您有所帮助。

【讨论】:

【参考方案3】:

感谢您的链接,llama591。

Haisergeant,如果您正在寻找的是使用第一个示例中的纹理的漂亮粗线,那么您需要使用 kPatternWidth 和 kPatternHeight 值,以及您在行 CGContextSetLineWidth(ctx, 4.0);.

我还必须修改我使用的纹理图像以确保其边缘上的空白非常少,因为这会在线条中引入白色间隙,如您的示例所示。

【讨论】:

以上是关于iphone:将纹理应用到 CGContextStrokePath的主要内容,如果未能解决你的问题,请参考以下文章

我可以将纹理应用到 UIToolbar 吗?

如何将纹理应用到四边形以纹理立方体?

text 将纹理应用于modelItem

javascript 将纹理应用于modelItem

如何将纹理应用于 THREE.ExtrudeGeometry?

Three.js – 将纹理应用于 Collada 网格会产生意想不到的结果