Ну, я точно определил, как сделать это максимально лаконично. Ниже я объясню, как этого добиться, и перечислю весь необходимый код:)
Чтобы позволить сенсорному взаимодействию выбрать пиксель, сначала добавьте UITapGestureRecognizer
к своему подклассу GLKViewController
(при условии, что вы хотите нажать на пиксель для выбора) со следующим целевым методом внутри этого класса. Вы должны сделать свой GLKViewController
подкласс UIGestureRecognizerDelegate
:
@interface GLViewController : GLKViewController <GLKViewDelegate, UIGestureRecognizerDelegate>
После создания экземпляра распознавателя жестов добавьте его в свойство view
(которое в GLKViewController
на самом деле является GLKView
):
// Inside GLKViewController subclass init/awakeFromNib:
[[self view] addGestureRecognizer:[self tapRecognizer]];
[[self tapRecognizer] setDelegate:self];
Установите целевое действие для вашего распознавателя жестов; Вы можете сделать это при создании с использованием определенного init...
, однако я создал свой с использованием Storyboard (он же «новый Интерфейсный Разработчик в XCode 4.2») и подключил его таким образом.
В любом случае, вот мое целевое действие для распознавателя жестов касания:
-(IBAction)onTapGesture:(UIGestureRecognizer*)recognizer {
const CGPoint loc = [recognizer locationInView:[self view]];
[self pickAtX:loc.x Y:loc.y];
}
Вызван метод выбора, который я определил в своем подклассе GLKViewController
:
-(void)pickAtX:(GLuint)x Y:(GLuint)y {
GLKView *glkView = (GLKView*)[self view];
UIImage *snapshot = [glkView snapshot];
[snapshot pickPixelAtX:x Y:y];
}
Для этого используется новый удобный метод snapshot
, который Apple любезно включил в GLKView
для получения UIImage
из базового EAGLContext
.
Важно отметить комментарий в документации snapshot
API, который гласит:
Этот метод должен вызываться всякий раз, когда ваше приложение явно
нуждается в содержании представления; никогда не пытайтесь непосредственно прочитать
содержимое базового кадрового буфера с использованием функций OpenGL ES.
Это дало мне ключ к пониманию того, почему мои более ранние попытки вызвать glReadPixels
при попытках доступа к пиксельным данным сгенерировали EXC_BAD_ACCESS
и индикатор, который вместо этого направил меня по правильному пути.
Вы заметите, что в моем pickAtX:Y:
методе, определенном минуту назад, я вызываю pickPixelAtX:Y:
для UIImage
. Это метод, который я добавил в UIImage
в пользовательской категории:
@interface UIImage (NDBExtensions)
-(void)pickPixelAtX:(NSUInteger)x Y:(NSUInteger)y;
@end
Вот реализация; это окончательный список кодов требуется. Код пришел от этого вопроса и был изменен в соответствии с полученным ответом:
@implementation UIImage (NDBExtensions)
- (void)pickPixelAtX:(NSUInteger)x Y:(NSUInteger)y {
CGImageRef cgImage = [self CGImage];
size_t width = CGImageGetWidth(cgImage);
size_t height = CGImageGetHeight(cgImage);
if ((x < width) && (y < height))
{
CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef bitmapData = CGDataProviderCopyData(provider);
const UInt8* data = CFDataGetBytePtr(bitmapData);
size_t offset = ((width * y) + x) * 4;
UInt8 b = data[offset+0];
UInt8 g = data[offset+1];
UInt8 r = data[offset+2];
UInt8 a = data[offset+3];
CFRelease(bitmapData);
NSLog(@"R:%i G:%i B:%i A:%i",r,g,b,a);
}
}
@end
Первоначально я попробовал некоторый связанный код, найденный в документе Apple API под названием: «Получение пиксельных данных из контекста CGImage», для которого требовалось 2 определения метода вместо этого 1, но требуется гораздо больше кода и есть данные типа void *
, за которые я не смог реализовать правильную интерпретацию.
Вот и все! Добавьте этот код в свой проект, затем, нажав на пиксель, он выведет его в виде:
R:24 G:46 B:244 A:255
Конечно, вы должны написать некоторые средства для извлечения этих значений RGBA int (которые будут в диапазоне 0 - 255) и использования их по своему усмотрению. Один из подходов состоит в том, чтобы вернуть UIColor
из вышеописанного метода, например, так:
UIColor *color = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f];