Обнаружение затрагивает только непрозрачные пиксели UIImageView, эффективно
Как бы вы обнаруживали касания только непрозрачных пикселей UIImageView
, эффективно?
Рассмотрим изображение, подобное приведенному ниже, отображаемое с помощью UIImageView
. Цель состоит в том, чтобы распознаватели жестов реагировали только тогда, когда прикосновение происходит в непрозрачной (черной в этом случае) области изображения.
![enter image description here]()
Идеи
- Переопределить
hitTest:withEvent:
или pointInside:withEvent:
, хотя этот подход может быть ужасно неэффективным, поскольку эти методы вызываются много раз во время события касания.
- Проверка прозрачности одного пикселя может привести к неожиданным результатам, поскольку пальцы больше одного пикселя. Проверка круговой области пикселей вокруг точки попадания или попытка найти прозрачный путь к краю может работать лучше.
Bonus
- Было бы неплохо различать внешние и внутренние прозрачные пиксели изображения. В этом примере прозрачные пиксели внутри нуля также должны считаться действительными.
- Что произойдет, если изображение имеет преобразование?
- Можно ли ускорить обработку изображений?
Ответы
Ответ 1
Здесь моя быстрая реализация: (на основе Получение значения альфа-пикселя для UIImage)
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
//Using code from /questions/35052/retrieving-a-pixel-alpha-value-for-a-uiimage
unsigned char pixel[1] = {0};
CGContextRef context = CGBitmapContextCreate(pixel,
1, 1, 8, 1, NULL,
kCGImageAlphaOnly);
UIGraphicsPushContext(context);
[image drawAtPoint:CGPointMake(-point.x, -point.y)];
UIGraphicsPopContext();
CGContextRelease(context);
CGFloat alpha = pixel[0]/255.0f;
BOOL transparent = alpha < 0.01f;
return !transparent;
}
Это предполагает, что изображение находится в том же координатном пространстве, что и point
. Если масштабирование продолжается, вам может потребоваться преобразовать point
, прежде чем проверять данные пикселя.
Появляется для работы довольно быстро для меня. Я измерял ок. 0,1-0,4 мс для вызова этого метода. Он не делает внутреннее пространство и, вероятно, не оптимален.
Ответ 2
В github вы можете найти проект Ole Begemann, который расширяет UIButton
, так что он обнаруживает только штрихи, в которых изображение кнопки не прозрачным.
Так как UIButton
является подклассом UIView
, его адаптация к UIImageView
должна быть простой.
Надеюсь, что это поможет.
Ответ 3
Ну, если вам нужно сделать это очень быстро, вам нужно предварительно просчитать маску.
Здесь, как его извлечь:
UIImage *image = [UIImage imageNamed:@"some_image.png"];
NSData *data = (NSData *) CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
unsigned char *pixels = (unsigned char *)[data bytes];
BOOL *mask = (BOOL *)malloc(data.length);
for (int i = 0; i < data.length; i += 4) {
mask[i >> 2] = pixels[i + 3] == 0xFF; // alpha, I hope
}
// TODO: save mask somewhere
Или вы можете использовать контекстное решение 1x1 для предварительного расчета маски.
Наличие маски означает, что вы можете проверить любую точку с затратами на один доступ к индексированной памяти.
Что касается проверки большей площади, чем одного пикселя, я бы проверял пиксели по кругу с центром в точке касания. Около 16 очков на круге должно быть достаточно.
Обнаружение также внутренних пикселей: еще один шаг предварительной калькуляции - вам нужно найти выпуклую оболочку маски.
Вы можете сделать это, используя алгоритм "Graham scan" http://softsurfer.com/Archive/algorithm_0109/algorithm_0109.htm
Затем либо заполните эту область в маске, либо сохраните многоугольник и вместо этого используйте тест "точка-в-полигон".
И, наконец, если изображение имеет преобразование, вам нужно преобразовать координаты точки из пространства экрана в пространство изображения, а затем вы можете просто проверить предварительно рассчитанную маску.