이번 튜토리얼에서는 컬러픽커를 만들어 보겠습니다. 컬러픽커의 기능은 사용자가 손가락으로 터치한 부분의 색상값을 알아 오는 것입니다.
이 예제를 통해서 배울 핵심 내용은 UIView내용을 이미지로 렌더링하고 렌더링 된 이미지의 픽셀값을 읽어 어는 것입니다. 참고로 이번 튜토리얼은 애플 개발자 문서의 기술Q&A 문서인 'Getting the pixel data from a CGImage object' 를 참고해서 만들었습니다.
컬러픽커를 보면 화려하고 고르게 색상이 분포되어 있습니다. 이런 색상 공간을 실시간으로 만들려면 시간이 오래 걸릴 수 있기 때문에 대부분 원하는 형태의 색상공간 이미지를 만든 다음에 해당 이미지를 사용하여 컬러픽커를 만듭니다. 이번 튜토리얼에서 사용할 색상 공간의 이미지는 아래와 같습니다.
이미지는 구글링을 통해서 얻었습니다 :) 손가락으로 영역을 터치하면 해당 색상으로 모서리가 둥근 작은 사각형의 레이어를 칠하고 색상의 RGB 값이 레이블에 표시됩니다. 아래는 결과 화면입니다.
원하는 결과를 얻기 위해서 우선 해야할 것은 화면의 내용을 이미지로 만드는 것입니다. 화면의 내용을 이미지로 만드는 방법은 정말 쉽습니다. UIKit이 아주 편리한 함수를 제공하기 때문인데요. 바로 UIGraphicsBeginImageContext 라는 함수입니다. 이 함수를 사용하면 비트맵컨텍스트를 만들고 만든 비트맵컨텍스트를 현재 컨텍스트로 설정합니다. 따라서 쿼츠의 모든 드로잉 연산이 비트맵에 적용됩니다. 그럼 화면의 모든 내용을 어떻게 비트맵컨텍스트에 그릴 수 있을까요? 코드를 우선 보겠습니다.
UIKit의 모습을 쉽게 이미지로 뽑을 수 있다는 것은 해당 이미지를 쉽게 OpenGLES의 텍스춰로 만들 수 있다는 의미입니다. 따라서 3D내에서 UIKit의 화려한 UI를 그대로 옮겨 표현할 수 있습니다. 참고로 증강현실앱으로 유명한 스캔서치가 이런 방법을 통해서 멋진 UI를 구현했으리라 생각합니다. 저도 증강현실앱을 만들 때 아주 유용하게 써 먹은 방법이랍니다.( 물론 터치에 반응하기 위해서 픽킹 등 세부적으로 더 많은 일들을 해줘야 하지만요.. )
전체 화면의 이미지를 만들었으니 픽셀값을 얻어와 봅시다. 쿼츠에서는 이미지의 픽셀값을 얻어오기 위해서는
원하는 색상공간( Grayscale, RGBA 등 )의 비트맵을 만들고 그 비트맵에 이미지를 그린 다음 해당 비트맵의 데이터를 얻어와야 합니다. UIImage에서 바로 다이렉트로 이미지의 Raw데이터에 접근하는 방법은 없습니다. 그럼 해당 코드를 보실까요?
소스코드
참고
[1] Technical Q&A QA1509 - Getting the pixel data from a CGImage object
이 예제를 통해서 배울 핵심 내용은 UIView내용을 이미지로 렌더링하고 렌더링 된 이미지의 픽셀값을 읽어 어는 것입니다. 참고로 이번 튜토리얼은 애플 개발자 문서의 기술Q&A 문서인 'Getting the pixel data from a CGImage object' 를 참고해서 만들었습니다.
컬러픽커를 보면 화려하고 고르게 색상이 분포되어 있습니다. 이런 색상 공간을 실시간으로 만들려면 시간이 오래 걸릴 수 있기 때문에 대부분 원하는 형태의 색상공간 이미지를 만든 다음에 해당 이미지를 사용하여 컬러픽커를 만듭니다. 이번 튜토리얼에서 사용할 색상 공간의 이미지는 아래와 같습니다.
이미지는 구글링을 통해서 얻었습니다 :) 손가락으로 영역을 터치하면 해당 색상으로 모서리가 둥근 작은 사각형의 레이어를 칠하고 색상의 RGB 값이 레이블에 표시됩니다. 아래는 결과 화면입니다.
클릭하시면 크게보입니다
원하는 결과를 얻기 위해서 우선 해야할 것은 화면의 내용을 이미지로 만드는 것입니다. 화면의 내용을 이미지로 만드는 방법은 정말 쉽습니다. UIKit이 아주 편리한 함수를 제공하기 때문인데요. 바로 UIGraphicsBeginImageContext 라는 함수입니다. 이 함수를 사용하면 비트맵컨텍스트를 만들고 만든 비트맵컨텍스트를 현재 컨텍스트로 설정합니다. 따라서 쿼츠의 모든 드로잉 연산이 비트맵에 적용됩니다. 그럼 화면의 모든 내용을 어떻게 비트맵컨텍스트에 그릴 수 있을까요? 코드를 우선 보겠습니다.
UIGraphicsBeginImageContext(바로 CALayer의 메서드인 renderInContext: 를 사용하면 됩니다. 이 메서드는 레이어의 내용을 컨텍스트에 렌더링합니다. 그리고 UIGraphicsGetImageFromCurrentImageContext 함수로 현재 컨텍스트에서 이미지를 만들 수 있습니다. 이 메서드와 함수들을 사용해서 정말 멋진 결과들을 만들 수 있습니다.
CGSizeMake(
CGRectGetWidth(screenBounds),
CGRectGetHeight(screenBounds))
);
//이미지컨텍스트에 메인뷰의 레이어 렌더링
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
//이미지로 얻어 냄
UIImage *mainViewImage =
UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIKit의 모습을 쉽게 이미지로 뽑을 수 있다는 것은 해당 이미지를 쉽게 OpenGLES의 텍스춰로 만들 수 있다는 의미입니다. 따라서 3D내에서 UIKit의 화려한 UI를 그대로 옮겨 표현할 수 있습니다. 참고로 증강현실앱으로 유명한 스캔서치가 이런 방법을 통해서 멋진 UI를 구현했으리라 생각합니다. 저도 증강현실앱을 만들 때 아주 유용하게 써 먹은 방법이랍니다.( 물론 터치에 반응하기 위해서 픽킹 등 세부적으로 더 많은 일들을 해줘야 하지만요.. )
전체 화면의 이미지를 만들었으니 픽셀값을 얻어와 봅시다. 쿼츠에서는 이미지의 픽셀값을 얻어오기 위해서는
원하는 색상공간( Grayscale, RGBA 등 )의 비트맵을 만들고 그 비트맵에 이미지를 그린 다음 해당 비트맵의 데이터를 얻어와야 합니다. UIImage에서 바로 다이렉트로 이미지의 Raw데이터에 접근하는 방법은 없습니다. 그럼 해당 코드를 보실까요?
#pragma mark -위의 코드는 애플의 개발자 문서를 참고한 것입니다. 코드의 자세한 설명은 'Getting the pixel data from a CGImage object' 와 '쿼츠2D프로그래밍 가이드'를 참고해 주세요. 아래의 코드는 위의 코드를 활용하여 원하는 위치의 픽셀 색상값을 반환하는 메서드입니다.
#pragma mark ARGB형식의 비트맵만들기
- (CGContextRef) createARGBBitmapContextFromImage:(CGImageRef) inImage
{
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void* bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
size_t pixelsWide = CGImageGetWidth(inImage);
size_t pixelsHigh = CGImageGetHeight(inImage);
bitmapBytesPerRow = (pixelsWide * 4);
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
//colorSpace = //CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
colorSpace = CGColorSpaceCreateDeviceRGB(); //CGColorCreateGenericRGB();
if(colorSpace == NULL)
{
fprintf(stderr, "Error allocating color space\n");
return NULL;
}
bitmapData = malloc(bitmapByteCount);
if(bitmapData == NULL)
{
fprintf(stderr, "Memory not allocated!");
CGColorSpaceRelease(colorSpace);
return NULL;
}
context = CGBitmapContextCreate(bitmapData,
pixelsWide,
pixelsHigh,
8,
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedFirst);
if(context == NULL)
{
free(bitmapData);
fprintf(stderr, "Context not created");
}
CGColorSpaceRelease(colorSpace);
return context;
}
#pragma mark -터치를 하거나 드래그를 하면 해당 위치의 색상값을 알아오고 레이어의 색상을 변경합니다.
#pragma mark 주어진 위치의 컬러값 얻기
- (UIColor*) colorAtLocation:(CGPoint)location
{
//전체 뷰를 이미지로 만든다
CGRect screenBounds = [[UIScreen mainScreen] bounds];
//이미지컨텍스트 생성 및 현재 컨텍스트로 변경
UIGraphicsBeginImageContext(CGSizeMake(CGRectGetWidth(screenBounds), CGRectGetHeight(screenBounds)));
//이미지컨텍스트에 메인뷰의 레이어 렌더링
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
//이미지로 얻어 냄
UIImage *mainViewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if(nil == mainViewImage)
{
return nil;
}
//ARGB형식의 비트맵을 만든다
CGContextRef context = [self createARGBBitmapContextFromImage:mainViewImage.CGImage];
if(context == NULL)
{
return nil;
}
size_t w = CGImageGetWidth(mainViewImage.CGImage);
size_t h = CGImageGetHeight(mainViewImage.CGImage);
CGRect rect = { {0,0}, {w, h} };
//이미지를 비트맵에 그린다.
CGContextDrawImage(context, rect, mainViewImage.CGImage);
//컬러를 추출한다
UIColor *color = nil;
unsigned char *data = CGBitmapContextGetData(context);
if(data != NULL)
{
int offset = 4*((w*round(location.y)) + round(location.x));
unsigned char alpha = data[offset];
unsigned char red = data[offset+1];
unsigned char green=data[offset+2];
unsigned char blue=data[offset+3];
color = [UIColor colorWithRed:(red/255.0f)
green:(green/255.0f)
blue:(blue/255.0f)
alpha:(alpha/255.0f)];
//레이블에 색상값을 표시한다.
//사실 이부분은 따로 빠져야 함
//커플링이 강해짐 ;;;
NSString *colorComponentsString = [NSString stringWithFormat:@"A:%d R:%d G:%d B:%d",
alpha,
red,
blue,
green];
[colorValue setText:colorComponentsString];
}
CGContextRelease(context);
if(data)
{
free(data);
}
return color;
}
#pragma mark -매번 터치하고 드래그할 때마다 이미지를 생성하기 때문에 성능상 좋은 코드는 아닙니다. 이번 예제의 경우 화면의 내용이 거의 변하지 않기 때문에 한 번 이미지를 만든 후 캐쉬해 두었다가 사용하는 것이 더 좋을 것 같습니다. 성능개선은 여러분에게 맡기고 이번 튜토리얼을 마무리하겠습니다. 그럼 해피코딩하세요 :)
#pragma mark 터치이벤트
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self.view];
UIColor *color = [self colorAtLocation:location];
[colorLayer setBackgroundColor:color.CGColor];
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self.view];
UIColor *color = [self colorAtLocation:location];
[colorLayer setBackgroundColor:color.CGColor];
}
소스코드
참고
[1] Technical Q&A QA1509 - Getting the pixel data from a CGImage object
'iOS개발팁' 카테고리의 다른 글
안전하게 현재 날짜 구하기. (0) | 2010.09.24 |
---|---|
싱글턴을 활용하자. (1) | 2010.07.30 |
아이폰과 아이패드 구분하기 (0) | 2010.07.29 |
아이폰 OpenGL ES 튜토리얼 01 (9) | 2010.07.19 |
아이폰 OpenGL ES 튜토리얼 시작 (0) | 2010.07.19 |