【问题标题】:Rendering a random noise CALayer with Core Image is generating strange artifacts使用 Core Image 渲染随机噪声 CALayer 会产生奇怪的伪影
【发布时间】:2026-02-25 14:15:02
【问题描述】:

我正在以编程方式创建一个CALayer 子类,该子类对其自身应用一点像素噪声。该代码的工作原理是它在图层中呈现噪点,但图像上有一个奇怪的伪影,我无法确定根本原因。

这是一张带有noiseOpacity 的示例图片,以使问题更加明显。

粉色框是UANoisyGradientLayer,是CAGradientLayer 的子类,包含以下位:

@interface UANoisyGradientLayer ()
    @property (nonatomic, retain) CIContext *noiseContext;
    @property (nonatomic, retain) CIFilter  *noiseGenerator;
    @property (nonatomic, retain) CIImage   *noiseImage;
@end

@implementation UANoisyGradientLayer

@synthesize noiseOpacity = _noiseOpacity, noiseImage;

- (id)init {
    self = [super init];
    if (self) {
        self.noiseOpacity = 0.10;
        self.noiseContext = [CIContext contextWithOptions:nil];         
        self.noiseGenerator = [CIFilter filterWithName:@"CIColorMonochrome"];
        [self.noiseGenerator setValue:[[CIFilter filterWithName:@"CIRandomGenerator"] valueForKey:@"outputImage"] forKey:@"inputImage"];
        [self.noiseGenerator setDefaults];

        self.noiseImage = [self.noiseGenerator outputImage];

    }
    return self;
}

- (void)drawInContext:(CGContextRef)ctx {

    [super drawInContext:ctx];

    CGRect extentRect = [self.noiseImage extent];
    if (CGRectIsInfinite(extentRect) || CGRectIsEmpty(extentRect)) {
        extentRect = self.bounds;
    }

    CGImageRef cgimg = [self.noiseContext createCGImage:self.noiseImage fromRect:extentRect];
    CGContextSetBlendMode(ctx, kCGBlendModeOverlay);
    CGContextSetAlpha(ctx, self.noiseOpacity);
    CGContextDrawImage(ctx, self.bounds, cgimg);
    CGImageRelease(cgimg);
}

基本上,我使用CIRandomGenerator 作为CIColorMonochrome 过滤器的输入在init 中创建CIImage。然后,当需要绘制它时,我使用self.bounds(范围为always infinite 或0)从中创建一个CGImageRef,并将其绘制到上下文中。

结果大部分都很好,但正如您在图像中看到的那样,似乎有一些拉伸正在发生。这里发生了什么?

【问题讨论】:

  • 有点晚了,但是你贴的代码和图片不符。在我的 iPad Mini 和模拟器中,这个类做了预期的事情:一个带有随机噪声的层。请发布实际重现错误的代码。
  • 发帖时确实如此。您可能正在使用不同的操作系统或 SDK。请参阅下面的解决方案。

标签: ios core-image noise cifilter ciimage


【解决方案1】:

虽然没有解决原始问题,但我从不同的角度解决了这个问题并复制了输出。我现在不是尝试生成self.bounds 大小的单个图像,而是生成一个只有 64x64 的图像,然后使用CGContextDrawTiledImage 平铺它。因为我现在将其调整为固定大小,所以我可以从 drawInContext: 方法中提取一些代码。最后,因为在 draw 方法中不再生成图像,所以我能够将其设为静态变量,因此它只会生成一次!这是完整的课程:

static CGImageRef   __noiseImage        = nil;
static CGFloat      __noiseImageWidth   = 0.0;
static CGFloat      __noiseImageHeight  = 0.0;

@implementation UANoisyGradientLayer

@synthesize noiseOpacity = _noiseOpacity;

- (id)init {
    self = [super init];
    if (self) {
        self.noiseOpacity = 0.1f;
        self.needsDisplayOnBoundsChange = YES;

        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            CIContext *noiseContext = [CIContext contextWithOptions:nil];

            CIFilter *noiseGenerator = [CIFilter filterWithName:@"CIColorMonochrome"];
            [noiseGenerator setValue:[[CIFilter filterWithName:@"CIRandomGenerator"] valueForKey:@"outputImage"] forKey:@"inputImage"];
            [noiseGenerator setDefaults];

            CIImage *ciImage = [noiseGenerator outputImage];

            CGRect extentRect = [ciImage extent];
            if (CGRectIsInfinite(extentRect) || CGRectIsEmpty(extentRect)) {
                extentRect = CGRectMake(0, 0, 64, 64);
            }

            __noiseImage = [noiseContext createCGImage:ciImage fromRect:extentRect];
            __noiseImageWidth = CGImageGetWidth(__noiseImage);
            __noiseImageHeight = CGImageGetHeight(__noiseImage);
        });
    }

    return self;
}

- (void)drawInContext:(CGContextRef)ctx {

    [super drawInContext:ctx];

    if (self.noiseOpacity > 0) {

        CGContextSaveGState(ctx);
        CGPathRef path = [[UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.cornerRadius] CGPath];
        CGContextAddPath(ctx, path);
        CGContextClip(ctx);
        CGContextSetBlendMode(ctx, kCGBlendModeOverlay);
        CGContextSetAlpha(ctx, self.noiseOpacity);


        CGContextDrawTiledImage(ctx, CGRectMake(0, 0, __noiseImageWidth, __noiseImageHeight), __noiseImage);

        CGContextRestoreGState(ctx);
    }
}

@end

注意CIColorMonochromeCIRandomGenerator 需要 iOS 6(或更新版本)。确保包含所需的框架(CoreImage.frameworkQuartzCore.framework)。

【讨论】:

  • 谢谢!正是我想要的。
最近更新 更多