Core Graphics - Quartz 2D 기본편

이 문서에서는 Quartz 를 이용하여 출력하는 방법을 소개합니다.
그리기 위치 설정, 회전, 확대, 축소에 관한 내용이 담겨 있습니다.
이번에는 기초적인 부분을 설명하고 다음 장에 더욱 자세한 사용법을 소개하겠습니다.

Core Graphics

https://developer.apple.com/documentation/coregraphics

Quartz 기반의 2D 그리기 도구로 Path , 안티얼라이싱, 그레디언트, 이미지, 색 관리, PDF 등등의 기능을 제공한다.
접두사로 CG를 사용한다.


용어 정리

CGContext

https://developer.apple.com/documentation/coregraphics/cgcontext

Quartz 의 2D 출력 대상이다. 그래픽 컨텍스트는 그리기 데이터와 그리기 위한 모든 장치 정보를 가지고 있다. 그리기 대상은 어플리케이션의 윈도, 비트맵 이미지, PDF, 프린터가 될 수 있다.

CGLayer

https://developer.apple.com/documentation/coregraphics/cglayer
그리기 행위를 재활용하기 위해서 Core Graphics 에서 쓰는 장치이다. View는 CGLayer 를 가지고 있고, 실제 그리기는 CGLayer 에 그린다. 저수준 API 라 속도가 빠르다.

Quartz

쿼츠 프레임워크는 2D 그리기 프레임워크이며 해상도와 장치에 독립적이다.
투명한 레이어, 패쓰 기반 그리기, 메모리(offscreen)에 그리기 : 더블 렌더링, 향상된 색 제어, 안티 얼라이싱, PDF 생성, 출력, 파싱 기능을 제공한다.
여담으로 iOS 11부터 PDFKit 이 제공된다. 그 이전에도 제공하던 API가 있지만 기능이 너무 제한적이었다.


iOS 에서 Quartz 사용하기

QuartzCore 를 import 한다.

Quartz 2D 그리기 모델은 두가지 좌표계가 있다.

  • User Space Coordinate 사용자 좌표계
  • 도큐먼트 페이지를 표시하는 좌표계. 사용자 좌표계는 실제 디바이스와 관계없는 부동소수점으로 표시된다. 어떤 문서를 실제 화면에 출력하거나 프린트 할 때, 쿼츠가 사용자 좌표계를 디바이스 좌표계로 맵핑해준다.
  • Device space 디바이스 좌표계
  • 실제 기기의 해상도를 표시하는 좌표계.

또한, 일반적인 모니터나 문서의 좌표계는 X 가 오른쪽으로 증가하고, Y가 아래로 증가한다. 그러나 Quartz CTM 은 X가 오른쪽으로 증가하고 Y가 위로 증가한다. Quartz CTM 을 변형시키면 아래 그림의 방향대로 움직인 다는 것을 염두에 두어야 한다.

CTM coordinate{:class=”img-responsive”}

CTM

사용자 영역의 그리기를 수정하기 위하여 CTM - the Current Transform Matrix 에 특정 연산을 할 수 있다. 그래픽 컨텍스트의 초기 CTM 은 단위행렬identity matrix 로 생성된다. CTM 을 수정하기 위하여 Quartz 의 변환 함수를 쓸 수 있다. 그러면 사용자 영역에 그리는 동작이 변경 된다. 바꿔 말하면 사용자 영역에 그리는 행위는 CTM 을 수정하는 행위가 수반된다.
CTM 은 행렬이니, 선형대수학의 행렬 계산을 알면 이해하는데 도움이 된다.

아래 예제 코드는 Objective-C 기준으로 작성되어 있다. Objective-C 는 C 와 호환되며, Quartz 코드는 구조적 프로그램 스타일로 작성되어 있다. Swift 최신 버전에 대응하는 Quatz 코드는 객체지향 방식으로 되어있다. 따라서 Objective-C 에서 CGContextXXX 로 쓸 수 있는 함수를 Swift 에서는 CGContext 인스턴스 메소드에서 대응하는 메소드를 찾을 수 있다.

예를 들면 아래 처럼 대응 할 수 있다.

Objective-C Swift
CGContextSaveGState(CGContextRef context) let context = CGContext…….
context.saveGState()

용어

  • Translate : 그리는 위치를 이동한다.
  • Rotate : 회전
  • Scale : 확대 , 축소
  • Concatenate : 두개의 행렬을 연결하는 것으로 행렬 곱연산을 통해 이루어진다.

CTM 변환

CTM을 변환하는 것은 컨텐츠를 변형시키는 것이 아니라 좌표계를 움직이는 것이다. 예를 들어, CTM 에 (10,10) 만큼 변화를 준다면 그리기 기준점이 (10,10)으로 이동하는 것이다. 그러므로 CTM 에 변형을 하기 전에 그 전의 상태를 기억해둬야 한다.

CGContextSaveGState(context)
CGContextRestoreGState(context)

이동(Translate)

CGContextTranslateCTM (myContext, 100, 50);

회전

회전의 단위는 radian 이다. 라디안 - 위키페디아
회전축의 기본 점은 (0,0) 이다. 만약 CTM을 이동했다면 이동한 지점이 회전축이 될 것이다.

CGContextRotateCTM (myContext, radians(–45.));  

// 라디안 - 각 변환
#include <math.h>
static inline double radians (double degrees) \
 {return degrees * M_PI/180;}

확대 축소

확대 축소 파라미터 X, Y 가 0~1사이면 축소가 되고, 1보다 크면 확대가 된다. X, Y 가 0보다 작을 경우 축으로 뒤집게 된다.

CGContextScaleCTM (myContext, .5, .75);

행렬 연결

CTM 변환을 연속적으로 수행하는 것인데, CTM에 행렬 곱을 함으로써 수행 할 수 있다.

void CGContextConcatCTM(CGContextRef c, CGAffineTransform transform);

다른 방법으로는, CTM 의 스테이트를 저장하지 않고(CGContextSaveGState를 호출 하지 않고) 연속적으로 CTM 변환 함수들을 호출 하면 된다.

예)
CGContextTranslateCTM (myContext, w,h);
CGContextRotateCTM (myContext, radians(-180.));

아핀 변형 Affine Transforms

Function Use
CGAffineTransformMakeTranslation 원점에서 x,y 만큼 이동하는 행렬을 만드는 함수
CGAffineTransformTranslate 기존에 있는 아핀 변형 행렬에 이동을 수행하는 함수
CGAffineTransformMakeRotation 좌표계를 얼만큼 회전 시킬 지 새로 정하는 행렬을 만드는 함수. 파라미터는 라디안.
CGAffineTransformRotate 기존에 있는 아핀 변형 행렬에 회전을 추가하는 함수
CGAffineTransformMakeScale 좌표계를 X축, Y축으로 각각 확대/축소를 하는 아핀 변환 행렬을 만드는 함수
CGAffineTransformScale 기존에 있는 아핀 변환 행렬에 확대/축소를 추가하는 함수
CGAffineTransformInvert 기존에 수행했던 변환을 되돌리고 싶을 때 사용. 일반적으로는 State를 저장했다 복원하기 때문에 잘 사용하지 않는다.
CGPointApplyAffineTransform 전체 공간에 대해 변환을 수행하는게 아닌 특정 포인트에 대해서 수행하는 함수
CGSizeApplyAffineTransform 사이즈에 대해서 위와 대응되는 함수
CGRectApplyAffineTransform 사각 영역에 대해 위와 대응되는 함수
CGAffineTransformMake 변환 행렬을 직접 만들 때 사용하는 함수

아핀 행렬의 수학적 기초

아핀 행렬은 3x3 행렬로 구성된다. 가장 오른쪽 열은 0, 0, 1 이 되는데 이것은 concatenation 을 위해 필요한 부분이다. 변환 행렬식은 2 * 3 행렬이면 되지만, 행렬곱은 선행 행렬의 열의 갯수와 후행 행렬의 행의 갯수가 같아야 하기 때문에 변환을 연쇄하기 위해서 3 * 3 행렬로 했다.

x,y 값을 아핀 변환 한 결과를 x’, y’ 라고 하고 아핀 행렬이 다음과 같이 되어있다면

Affine Matrics

x’ = ax+cy+tx
y’ = bx+dy+ty

이 된다.

단위 행렬

단위 행렬


1 0 0
0 1 0
0 0 1


위 식에 입력해보면

x’ = x1 + y0 + 0 = x
y’ = x1 + y1 + 0 = y

아핀 이동


1 0 0
0 1 0
tx ty 1


x’ = x + tx
y’ = y + ty

아핀 확대 축소


sx 0 0
0 sy 0
0 0 1


x’ = x * sx
y’ = y * sy

회전


cosa | sina | 0
-sina | cosa | 0
0 | 0 | 1


x’ = xcosa-ysina
y’ = xsina + ycosa

Concatenate

둘 이상의 행렬을 연환하여 계산하는 것인데, 행렬 곱과 같다. 사칙연산의 곱셈과는 다르게 행렬 곱은 비가환적이므로(a * b 와 b * a 가 같지 않다) 순서에 주의해야 한다.

위 내용은 위키페디아의 아핀 행렬을 보면 직관적으로 알 수 있습니다.
애플 문서 Quartz : The Math behind the Matrics 에도 설명되어 있으니, 도표만 보면 바로 이해가 가실겁니다.

참고

Quartz Introduction

Draing printing iOS

Drawing with Quartz 2d

CTM 변형