Хит-тестирование с использованием CALayer с использованием альфа-свойств содержимого CALayer

Я пишу игру для Mac, используя Cocoa. В настоящее время я внедряю тестовое тестирование и обнаружил, что CALayer предлагает тестирование на удар, но, похоже, не реализует альфа-свойства. Поскольку у меня иногда много CALayers сложены друг на друга, мне действительно нужно найти способ определить, на что собственно намеревался пользователь нажимать.

Я думаю, если бы я мог каким-то образом получить массив, содержащий указатели на все CALayers, которые содержат точку щелчка, я мог бы фильтровать их каким-то образом. Однако единственный способ, которым я до сих пор создавал массив:

NSMutableArray* anArrayOfLayers = [NSMutableArray array];
    for (CALayer* aLayer in mapLayer.sublayers)
    {
        if ([aLayer containsPoint:mouseCoord])
            [anArrayOfLayers addObject:aLayer];
    }

Затем отсортируйте массив по значениям CALayer z, а затем проверьте, является ли пиксель в местоположении альфа или нет. Тем не менее, между сортировкой и альфа-проверкой это кажется невероятным боем производительности. (Как бы вы даже проверили альфу?)

Есть ли способ сделать это?

Ответ 1

Что-то, на что я наткнулся, почесывая голову над подобной проблемой, заключается в том, что CALayer использует containsPoint:, когда вы отправляете его hitTest:

Его поведение по умолчанию - это тест против границ, но мы можем переопределить его и проверить его на альфа-канал, а просто использовать CALayer, встроенный в тестирование hit, чтобы справиться с остальными:

- (BOOL) containsPoint:(CGPoint)p 
{
    return CGRectContainsPoint(self.bounds, p) && !ImagePointIsTransparent(self.contents, p)) return YES;
}

Здесь обсуждается тестирование одного пикселя alpha на Извлечение значения альфа-пикселя для UIImage

Это сработало для моих целей:

static BOOL ImagePointIsTransparent(CGImageRef image, CGPoint p)
{
    uint8_t alpha;

    CGContextRef context = CGBitmapContextCreate(&alpha, 1, 1, 8, 1, NULL, kCGImageAlphaOnly);
    CGContextDrawImage(context, CGRectMake(-p.x, -p.y, CGImageGetWidth(image), CGImageGetHeight(image)), image);
    CGContextRelease(context);

    return alpha == 0;
}

(Если вы используете renderInContext: для рисования на CALayer, а не для установки его содержимого, то это будет сложнее. Это может быть полезно в этом случае: http://www.cimgf.com/2009/02/03/record-your-core-animation-animation/)