본문 바로가기
SKIA

2.4 Canvas drawImage

by SimonLee 2025. 5. 5.

 

함수 설명
drawImage(image, x, y) 위치 (x, y)에 이미지를 그린다. 기본 샘플링, paint 없음
drawImage(image, x, y, sampling) 샘플링 방식 지정 (예: 선형 보간 등)
drawImage(image, x, y, sampling, paint) 투명도, 효과, blend 설정 가능
drawImage(image, dstRect, sampling, paint) 이미지를 dstRect에 스케일링해서 그림

 

 

함수 설명
drawImageRect(image, dstRect, sampling) 이미지를 dstRect에 맞게 스케일링해서 그림. Paint 없음
drawImageRect(image, dstRect, sampling, paint) 스케일링 + 투명도/효과/블렌드 적용
drawImageRect(image, dstRect, sampling, paint, constraint) constraint 설정 추가 (보통 kStrict 사용)
drawImageRect(image, srcRect, dstRect, sampling) 이미지의 일부분(srcRect)을 잘라서 dstRect로 그림
drawImageRect(image, srcRect, dstRect, sampling, paint) 위 + 페인트 효과 적용
drawImageRect(image, srcRect, dstRect, sampling, paint, constraint) src 잘라서 dst에 스케일, 효과 적용, constraint 명시

 


그려보자

void draw(SkCanvas* canvas) {
   // sk_sp<SkImage> image;
   SkImage* imagePtr = image.get();
   canvas->drawImage(imagePtr, 0, 0);
   SkPaint paint;
   canvas->drawImage(imagePtr, 80, 0, SkSamplingOptions(), &paint);
   paint.setAlpha(0x80);
   canvas->drawImage(imagePtr, 160, 0, SkSamplingOptions(), &paint);
}

 

생성자 형태 의미
SkSamplingOptions() 기본값 = Nearest (최근접 보간)
SkSamplingOptions(filter) filter는 SkFilterMode
SkSamplingOptions(filter, mipmap) 보간 + mipmap 여부

filter 종류 : kNearest, KLinear

mipmap 종류 : kNone, kNearest, kLinear

 

함수 설명
drawImage(image, x, y) 위치 (x, y)에 이미지를 그린다. 기본 샘플링, paint 없음
drawImage(image, x, y, sampling) 샘플링 방식 지정 (예: 선형 보간 등)
drawImage(image, x, y, sampling, paint) 투명도, 효과, blend 설정 가능
drawImage(image, dstRect, sampling, paint) 이미지를 dstRect에 스케일링해서 그림

 

image를 설정해 주지 않았는데, 디폴트 이미지가 저렇게 부채꼴같은 모양인가보네 - -';'

첫번째 이미지는 원본

두번째 이미지는 Nearest 샘플링 방식

세번째는 Nearest + 투명도 0.8로 준 방식


그려보자

void draw(SkCanvas* canvas) {
    // sk_sp<SkImage> image;
    for (auto i : { 1, 2, 4, 8 } ) {
        canvas->drawImageRect(image.get(), SkRect::MakeLTRB(0, 0, 100, 100),
                SkRect::MakeXYWH(i * 20, i * 20, i * 20, i * 20), SkSamplingOptions(),
                nullptr, SkCanvas::kStrict_SrcRectConstraint);
    }
}

 

 

함수 설명
drawImageRect(image, dstRect, sampling) 이미지를 dstRect에 맞게 스케일링해서 그림. Paint 없음
drawImageRect(image, dstRect, sampling, paint) 스케일링 + 투명도/효과/블렌드 적용
drawImageRect(image, dstRect, sampling, paint, constraint) constraint 설정 추가 (보통 kStrict 사용)
drawImageRect(image, srcRect, dstRect, sampling) 이미지의 일부분(srcRect)을 잘라서 dstRect로 그림
drawImageRect(image, srcRect, dstRect, sampling, paint) 위 + 페인트 효과 적용
drawImageRect(image, srcRect, dstRect, sampling, paint, constraint) src 잘라서 dst에 스케일, 효과 적용, constraint 명시

 

Constraint 경우 kStrict_SrcRectConstratin, kFast_SrcRectConstraint 두개가 있는데,

Strict의 경우 정확하게 Src영역을 잘라서 그린 반면, Fast의 경우 바깥 픽셀이 보일수는 있음 하지만 성능 빠름.

 

 

728x90

그려보자

 

void draw(SkCanvas* canvas) {
    uint32_t pixels[][2] = { { 0x00000000, 0x55555555},
                             { 0xAAAAAAAA, 0xFFFFFFFF} };
    SkBitmap bitmap;
    bitmap.installPixels(SkImageInfo::MakeN32Premul(2, 2),
            (void*) pixels, sizeof(pixels[0]));
    sk_sp<SkImage> image = bitmap.asImage();
    SkPaint paint;
    canvas->scale(4, 4);
    for (auto color : { SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN } ) {
        paint.setColorFilter(SkColorFilters::Blend(color, SkBlendMode::kPlus));
        canvas->drawImageRect(image, SkRect::MakeWH(2, 2), SkRect::MakeWH(16, 16),
                              SkSamplingOptions(), &paint, SkCanvas::kStrict_SrcRectConstraint);
        canvas->translate(16, 0);
    }
}

 

컬러필터에 적용된 이미지가 출력이 된다.

 

pixels[2][2] 구성은 다음과 같다.

- 완전 투명, 흐린 회색 (0x55..) , 반투명 회색 (0xAA..), 불투명 흰색 (0xFF)

 

bitmap.installPixels(SkImageInfo::MakeN32Premul(2, 2), pixels, ...);

- installPixels 함수는 외부 메모리의 픽셀데이터를 SkBitmap에 연결하는 함수

- 첫번째 인자 SkImageInfo가 MakeN32Premul() 함수라서 pre multplied alpha 적용이 됨

- (0, 0, 0, 0), (85, 85, 85, 85), (170, 170, 170, 170), (255, 255, 255, 255)

- RGB 값을 알파값으로 곱해주면

- (0, 0, 0, 0), (28, 28, 28, 85(0.3)),  (113, 113, 113, 170), (255, 255, 255, 255)

 

bool SkBitmap::installPixels(
    const SkImageInfo& info,  // 픽셀의 포맷 및 크기 정보
    void* pixels,             // 외부 메모리 포인터 (픽셀 데이터)
    size_t rowBytes,          // 한 줄(1 row)의 바이트 수 (stride)
    SkBitmap::ReleaseProc proc = nullptr,  // (선택) 해제 함수
    void* context = nullptr                // (선택) 해제 함수에 전달할 사용자 데이터
);

 

 

SkImage image = bitmap.asImage()

- 비트맵을 이미지로 변환

 

canvas->scale(4, 4)

- 좌표계를 x, y 4배 확대

 

paint.setColorFilter(SkColorFilters::Blend(color, SkBlendMode::kPlus));

- color 값은 순차적으로 빨강색, 파랑색 초록색이 되고, 블렌딩 모드가 kPlus라 이전 색상과 더해지게 된다.

- 255를 넘는 값은 클램핑이 된다.

- Color가 빨강색인 경우

- (0, 0, 0, 0), (28, 28, 28, 85(0.3)),  (113, 113, 113, 170), (255, 255, 255, 255) + Red Color(255, 0, 0, 255)

- (255, 0, 0, 255), (255, 28, 28, 255), (255, 113, 113, 255), (255, 255, 255, 255)

 

canvas->drawImageRect(image, SkRect::MakeWH(2, 2), SkRect::MakeWH(16, 16), ...)

- 이미지 4개의 사각영역이 블렌딩(Plus) 후 렌더링 된다.

- 빨강, 어두운 빨강, 밝은 빨강, 흰색으로 출력이 된다.

 

scale을 4배로 해서, 타켓 width, height 가 16 --> 64 픽셀로 그려진다.

translate(16, 0) 도 16 --> 64로 이동하게 된다.

 

Blend Mode 설명 효과
kSrcOver 기본 모드 (Source over Dest) 색을 위에 덮음. 알파에 따라 원본 보임
kPlus 단순 더하기 dst + src → 255 이상은 클램핑
kMultiply 곱하기 dst * src / 255 → 어두워짐
kScreen 반전 곱 255 - ((255 - dst) * (255 - src) / 255) → 밝게 보정
kOverlay 어두운 부분은 multiply, 밝은 부분은 screen 대비 강조 (포토샵 스타일)
kDarken 둘 중 어두운 쪽 선택 min(dst, src)
kLighten 둘 중 밝은 쪽 선택 max(dst, src)
kSrc src 그대로 사용 (덮어쓰기) 지정한 색(color)로 완전히 대체
kDst 원래 픽셀 유지 color는 무시됨
kXor src와 dst가 겹치는 부분은 투명 이상한 효과. 실사용 적음

 

 


그려보자

 

 

void draw(SkCanvas* canvas) {
    uint32_t pixels[][4] = {
            { 0xFFFF0000, 0xFFFF0000, 0xFFFF0000, 0xFFFF0000 },
            { 0xFFFF0000, 0xFF000000, 0xFFFFFFFF, 0xFFFF0000 },
            { 0xFFFF0000, 0xFFFFFFFF, 0xFF000000, 0xFFFF0000 },
            { 0xFFFF0000, 0xFFFF0000, 0xFFFF0000, 0xFFFF0000 } };
    SkBitmap redBorder;
    redBorder.installPixels(SkImageInfo::MakeN32Premul(4, 4),
            (void*) pixels, sizeof(pixels[0]));
    sk_sp<SkImage> image = redBorder.asImage();
    SkSamplingOptions sampling;
    for (auto constraint : {
            SkCanvas::kFast_SrcRectConstraint,
            SkCanvas::kStrict_SrcRectConstraint,
            SkCanvas::kFast_SrcRectConstraint } ) {
        canvas->drawImageRect(image.get(), SkRect::MakeLTRB(1, 1, 3, 3),
                SkRect::MakeLTRB(16, 16, 48, 48), sampling, nullptr, constraint);
        sampling = SkSamplingOptions(SkFilterMode::kLinear);
        canvas->translate(80, 0);
    }
}

 

pixels를 4,4 픽셀로 비트맵으로 만들면 다음과 같다.

비트맵에서 (1, 1) ~ (3, 3) 영역 정사각형을 (16, 16), (48, 48) 로 확대시켜서 렌더링을 한 결과이다.

3개의 차이점은 

첫번째 샘플링 방식 Nearest, kFast_SrcRectConstraint : 가장 가까운 픽셀을 취함.

두번째 샘플링 방식 Linear, kStrict_SrcRectContration : source 영역을 정확하게 클립핑하여 주변 빨강 안섞임.

세번째 샘플링 방식 Linear, kFast_.xxx : 주변 빨강 픽셀 샘플링되어 섞임.


그려 보자.

void draw(SkCanvas* canvas) {
    SkIRect center = { 10, 10, 20, 20 };
    SkBitmap bitmap;
    bitmap.allocPixels(SkImageInfo::MakeN32Premul(30, 30));
    SkCanvas bitCanvas(bitmap);
    SkPaint paint;
    SkSamplingOptions sampling;
    SkColor gray = 0xFF000000;
  
    // 비트맵에 렌더링
    int left = 0;
    for (auto right: { center.fLeft, center.fRight, bitmap.width() } ) {
        int top = 0;
        for (auto bottom: { center.fTop, center.fBottom, bitmap.height() } ) {
            paint.setColor(gray);
            bitCanvas.drawIRect(SkIRect::MakeLTRB(left, top, right, bottom), paint);
            gray += 0x001f1f1f;
            top = bottom;
        }
        left = right;
    }
  
    sk_sp<SkImage> image = bitmap.asImage();
    canvas->drawImageNine(image.get(), center, SkRect::MakeWH(100, 100),
                          SkFilterMode::kLinear, nullptr);
}

 

비트맵에 렌더링을 하고, 비트맵을 캔버스에 렌더링하는 구조이다.

 

MakeN32Premul 함수는 ARGB32 비트로 width = 30, height = 30 으로 SkImageInfo 타입을 리턴한다.

함수 이름 알파 방식 설명
MakeN32Premul Premultiplied 알파가 색상에 곱해짐.
GPU 연산 효율적, 기본 사용
MakeN32Unpremul Unpremultiplied 알파가 색상에 안곱해짐
MakeN32Opaque Opaque 알파 없음, 모두 불투명

 

allocPixels 함수는 SkImageInfo 타입을 받아서 비트맵에 픽셀을 할당한다.

 

SkCanvas bitCanvas(bitmap)

- 비트맵으로 캔버스를 생성하면, 캔버스 렌더링을 하게 되면 비트맵에 그려진다.

 

구역을 나누게 되는 알고리즘인데

auto right : { 20, 40, 60 } 이면 3번 루프에 right에 20, 40,60 순서대로 할당이 된다.

 

9등분이 되고, drawIRect는 정수기반 사각형 구조체이다.

- cropping 하기에 알맞음.

 

bitCanvas.drawIRect(LTRB, paint)

- 비트맵에 렌더링 된다.

 

sk_sp<SkImage> image = bitmap.asImage();

- 비트맵을 이미지로 변환한다.

canvas->drawImageNine(image.get(), center, SkRect::MakeWH(100, 100),
                          SkFilterMode::kLinear, nullptr);

drawImageNine 함수는 이미지를 9개의 영역으로 나누어서 중앙은 늘리고, 모서리는 유지하면서 

다른 크기로 유연하게 그리는 함수

30, 30 이미지를 100, 100으로 늘리므로 양측 모서리 영역은 크기가 유지되고, 나머지 영역이 커진다.

중앙영역은 센터 영역이다.

이미지가 확대 되었을때, 

5번 영역은 중앙 영역 양방향으로 커지게 되고,

2, 4, 6, 8 영역은 한방향으로만 커지게 된다.

1,3,7,9 영역은 크기가 유지 된다.

- 센터의 LTRB(10, 10) (20, 20) 이므로, 1,3,7,9 의 width, height는 10 pixel에서 증가하지 않는다.

canvas->drawImageRect(image.get(), 
                          SkRect::MakeLTRB(0, 0, 30, 30),
                	      SkRect::MakeLTRB(0, 0, 100, 100), 
                          sampling, nullptr, 
                          SkCanvas::kStrict_SrcRectConstraint );

drawImageRect를 사용하면 이미지를 확대 할수는 있지만, 모서리 영역은 무시하고 전체적으로 커진다.

728x90

'SKIA' 카테고리의 다른 글

2.6 Canvas CheckerBoard  (0) 2025.05.13
2.5 Canvas read&write Pixels, saveLayer  (0) 2025.05.09
2.3 Canvas Drawable, DRRect  (0) 2025.05.05
2.1.1 Canvas Clip Rect & Region  (0) 2025.05.02
2.1.0 Canvas Clip Path  (0) 2025.04.30