본문 바로가기
SKIA

2.1.0 Canvas Clip Path

by SimonLee 2025. 4. 30.

 

1. ClipRect


 

겹쳐서 그린 사각형

void draw(SkCanvas* canvas) {
    canvas->save();
    canvas->clipRect(SkRect::MakeWH(256, 128));
    canvas->clear(SkColorSetARGB(0x80, 0xFF, 0x00, 0x00));
    canvas->restore();
    canvas->save();
    canvas->clipRect(SkRect::MakeWH(150, 192));
    canvas->clear(SkColorSetARGB(0x80, 0x00, 0xFF, 0x00));
    canvas->restore();
    canvas->clipRect(SkRect::MakeWH(75, 256));
    canvas->clear(SkColorSetARGB(0x80, 0x00, 0x00, 0xFF));
}

 

clipRect는 파라메터로 주어진 범위 영역만 사용하고, 나머지는 클리핑 한다는 의미이다.

MakeWH는 (0,0) ~ (256, 128) 의 범위를 가지고 있는데,

해당 범위만 렌더링을 하고 나머지는 렌더링을 하지 않는다.

 

Save, restore 함수는 현 상태의 Matrix, Clip 상태를 저장 및 복원을 하는 함수다.

restore 실행 후 항상 초기상태를 유지하고 있다.

 

3개의 사각형을 그리는데 다음 순서로 그린다.

1. (0, 0) ~ (256, 128) 빨강색 

2. (0, 0) ~ (150, 192) 초록색

3. (0, 0) ~ (75, 256)  파랑색

 

해당 순서로 그려서 겹치는 부분을 이해하기 바란다.

SkColorSetARGB는 각 파라메터가 Alpha, Red, Green, Blue 순서로 되어 있다.

 

 

그러면 여기서 restore() 함수를 주석처리하면 어떻게 될까?

-----------------------------------------------------------------------

클립 영역이 Intersection 되어서, 세 구역의 공통 교차지역만 렌더링이 된다.

교차된 구역

 

2. SkPath ClipPath


SkPathFillType 종류

FillType 내부 판단 기준 겹칠 때 특징 사용 용도
kWinding 경로 방향(시계/반시계) 누적 합 계산 같은 방향이면 강조, 반대 방향이면 상쇄 기본값, 복잡한 경로 표현
kEvenOdd 경로 교차 횟수가 홀수/짝수 여부 홀수 교차면 내부, 짝수면 외부 단순 구멍 표현, SVG 스타일
kInverseWinding kWinding의 반대 (바깥이 내부) 외부가 내부가 됨 외곽 마스킹 효과
kInverseEvenOdd kEvenOdd의 반대 (바깥이 내부) 외부가 내부가 됨 외곽 영역 마스킹

 

 

SkClipOp 종류

ClipOP 의미 작동 방식 설명 사용 용도
SkClipOp::kIntersect 교집합 현재 클립 영역과 주어진 path/rect의 교집합만 남김 특정 영역 안에서만 그리도록 제한
SkClipOp::kDifference 차집합 현재 클립 영역에서 주어진 path/rect를 빼버림 구멍을 뚫거나 제외 영역 만들 때 사용

 

 

 

아래 모양을 그려보자 1

void draw(SkCanvas* canvas) {
    SkPaint paint;
    paint.setAntiAlias(true);
    SkPath path;
    path.addRect({20, 15, 100, 95});
    path.addRect({50, 65, 130, 135});
    path.setFillType(SkPathFillType::kWinding);
    canvas->save();
    canvas->clipPath(path, SkClipOp::kIntersect);
    canvas->drawCircle(70, 85, 60, paint);
    canvas->restore();
    canvas->translate(100, 100);
    path.setFillType(SkPathFillType::kEvenOdd);
    canvas->clipPath(path, SkClipOp::kIntersect);
    canvas->drawCircle(70, 85, 60, paint);
}

 

SkPath

점, 선, 곡선 "연속된 경로"를 표현하는 클래스로, 도형(사각형, 원, 다각형, 커브)를 정의하거나

복잡한 클리핑 영역을 만들때 사용.

 

path.addRect()

addRect를 호출하게 되면, 내부적으로 moveTo, lineTo, quadTo 를 사용하여 영역을 생성한다.

 

path.setFillType(SkPathFillType::kWinding);

SkPath의 setFillType 함수를 사용하여 영역을 결정한다.

 

cnavas.clipPath(SkPath path, SkClipOp op)

canvas.clipRect와 같이 해당 경로의 영역만 사용하기 위해 클리핑 하기 위해서는 clipPath를 호출한다.

현재 캔버스에서 클립 명령을 실행을 전에 안했으니, Source 영역은 전체 영역!!!

--> 사각형 두개의 영역 설정 됨.

 

canvas->drawCircle(70, 85, 60, paint)

자 이제 원을 그려본다. x, y, radius 임. 

사각형 두개의 영역에서만 그릴 수 있기에 아래처럼 나옴.

 

canvas->restore();

하나를 더 그릴껀데, 그리기전에 해야 할일은 Clip상태를 초기화 시켜줘야 한다.

 

canvas->translate(100, 100);

겹치지 않기 위해 가운데로 이동한다.

 

path.setFillType(SkPathFillType::kEvenOdd);

두 사각형의 겹치는 구간은 그리지 않음. 겹치지 않는 구간만 그림.

kEvenOdd 겹치는 구간은 안그림

 

canvas->drawCirvle(70, 85, 60, paint)

클리핑된 영역에서 원을 렌더링하면 아래와 같은 모양 ^^

 

 

아래 모양을 그려보자 2

void draw(SkCanvas* canvas) {
    SkPaint paint;
    paint.setAntiAlias(true);
    SkPath path;
    SkPoint poly[] = {{20, 20}, { 80, 20}, { 80,  80}, {40,  80},
                      {40, 40}, {100, 40}, {100, 100}, {20, 100}};
    path.addPoly(poly, std::size(poly), true);
    path.setFillType(SkPathFillType::kWinding);
    
  	canvas->save();
    canvas->clipPath(path, SkClipOp::kIntersect);
    canvas->drawCircle(50, 50, 45, paint);// SK_FOLD_START
    canvas->restore();
    canvas->translate(100, 100);
    path.setFillType(SkPathFillType::kEvenOdd);
    canvas->clipPath(path, SkClipOp::kIntersect);
    canvas->drawCircle(50, 50, 45, paint);
}

 

 

path.addPoly(poly, std::size(poly), true);

여덜개의 꼭지점을 갖는 다각형을 만듬,

true로 설정하여 닫힌 경로가 됨

 

path.setFillType(SkPathFillType::kWinding);

Winding은 경로에 따라서 안쪽을 정의함. 합집합.

 

canvas.clipPath(path, skClipOp::kIntersect);

캔버스에서 그린 영역을 path 영역과 교차영역을 제외하고 클립핑

--> 교차 영역만 렌더링

 

path.setFillType(SkPathFillType::kEvenOdd);

겹치는 구간은 제외하여 렌더링

--> 내부 사각형이 제외됨.

 

 

아래 모양을 그려보자 3

void draw(SkCanvas* canvas) {
    SkPaint paint;
    paint.setAntiAlias(true);
    SkPath path;
    path.addRect({20, 30, 100, 110});
    path.setFillType(SkPathFillType::kInverseWinding);
    canvas->save();
    canvas->clipPath(path, SkClipOp::kDifference, false);
    canvas->drawCircle(70, 100, 60, paint);
    canvas->restore();
    
    canvas->translate(100, 100);
    path.setFillType(SkPathFillType::kWinding);
    canvas->clipPath(path, SkClipOp::kIntersect, false);
    canvas->drawCircle(70, 100, 60, paint);
}

 

 

path.addRect({20, 30, 100, 110})

LTRB 방식 (20, 30) ~ (100, 110)

 

path.setFillType(SkPathFillType::kInverseWinding)

사각형 외부 빨강색 영역

 

cnavas->clipPath(path, SkClipOp::kDifference, false)

빨강색의 영역을 제외 -> 파랑색 영역

 

canvas->drawCircle(70, 100, 60, paint);

초록색 영역

 

path.setFillType(SkPathFillType::kWinding);

canvas->clipPath(path, SkClipOp::kIntersect, false);

 

사각형 내부 영역의 intersect 이므로 동일한 영역을 알 수 있다.

그림을 그려보면 이해가 될 것이다.

'SKIA' 카테고리의 다른 글

2.5 Canvas read&write Pixels, saveLayer  (0) 2025.05.09
2.4 Canvas drawImage  (0) 2025.05.05
2.3 Canvas Drawable, DRRect  (0) 2025.05.05
2.1.1 Canvas Clip Rect & Region  (0) 2025.05.02
1. SkCanvas Overview  (0) 2025.04.29