본문 바로가기
SKIA

2.5 Canvas read&write Pixels, saveLayer

by SimonLee 2025. 5. 9.

ReadPixels

void draw(SkCanvas* canvas) {
    // 캔버스를 파란색으로 클리어
    canvas->clear(SK_ColorBLUE);

    SkPaint paint;
    // 중심 (32,32), 반지름 28짜리 원을 그림 (기본색 = 검정)
    canvas->drawCircle(32, 32, 28, paint);

    // 64x64 크기, BGRA8888 포맷, Premultiplied 알파의 이미지 정보 생성
    SkImageInfo info = SkImageInfo::Make(64, 64, kBGRA_8888_SkColorType, kPremul_SkAlphaType);

    // info에 맞는 크기의 메모리 할당 (64x64x4 = 16384바이트)
    sk_sp<SkData> data(SkData::MakeUninitialized(info.minRowBytes() * info.height()));

    // 읽은 픽셀 데이터를 넣기 전 버퍼를 0으로 초기화 (깨끗한 상태로)
    sk_bzero(data->writable_data(), info.minRowBytes() * info.height());

    // 4개의 서로 다른 위치에서 readPixels 수행 (일부는 캔버스 바깥)
    for (int x : { 32, -32 } ) {
        for (int y : { 32, -32 } ) {
            // 캔버스의 (x,y)를 좌상단으로 하여 64x64 크기의 픽셀 데이터를 읽음
            // 단, 읽은 데이터는 모두 같은 data 버퍼에 덮어쓰기됨
            canvas->readPixels(info, data->writable_data(), info.minRowBytes(), x, y);
        }
    }

    // 읽은 픽셀 데이터를 기반으로 래스터 이미지 생성
    sk_sp<SkImage> image = SkImages::RasterFromData(info, data, info.minRowBytes());

    // 이미지 데이터를 다시 (0,0)에 출력 (버퍼에 남은 결과 확인용)
    canvas->drawImage(image, 0, 0);
}

 

728x90

 

SkImageInfo 구조체 

struct SkImageInfo {
    int                 fWidth;       // 이미지 가로 길이 (픽셀)
    int                 fHeight;      // 이미지 세로 길이
    SkColorType         fColorType;   // 픽셀 포맷 (예: kBGRA_8888_SkColorType)
    SkAlphaType         fAlphaType;   // 알파 타입 (Premul, Unpremul, Opaque)
    sk_sp<SkColorSpace> fColorSpace;  // 색공간 (sRGB, linear 등)
};

// 한줄을 저장하는데 최소 바이트수
size_t SkImageInfo::minRowBytes() return width() * bytesPerPixel()

 

SkData 구조체

class SkData : public SkRefCnt {
public:
    // 데이터 크기 반환 (바이트 단위)
    size_t size() const;
    // 읽기 전용 포인터 반환
    const void* data() const;
    // 쓰기 가능한 포인터 반환 (단, 불변성 보장 깨짐 — MakeUninitialized 등에서만 의미 있음)
    void* writable_data();
	....
};

 

SkImageInfo::Make 함수

- Skia 에서 이미지의 픽셀 형식, 크기, 알파, 색 공간을 지정하여 SkImageInfo 객체를 생성

파라메터 설명
width 이미지의 가로 크기 (픽셀 단위)
height 이미지의 세로 크기
colorType 픽셀의 색상 포맷 (kRGBA_8888_SkColorType, kBGRA_8888_SkColorType 등)
alphaType 알파 타입 (kPremul_SkAlphaType, kUnpremul_SkAlphaType, kOpaque_SkAlphaType)
colorSpace 색 공간 (SkColorSpace::MakeSRGB() 등), 기본은 nullptr (sRGB로 간주)

 

 

SkData::MakeUnInitialized(size_t length)

- length 바이크 크기의 메모리 블록을 할당만 하고, 초기화 하지 않음. (성능이 중요한 경우 사용)

- 반환 값은 sk_sp<SkData>

 

sk_bzero(void *buffer, size_t size)

- Skia 내부에서 사용하는 메모리 초기화 함수로 memset(ptr, 0, size)와 동일한 역할을 한다.

파라메터 타입 설명
buffer void* 0으로 초기화할 메모리의 시작 주소
size size_t 0으로 설정할 바이트 수

 

readPixels(info, data->writable_data(), info.minRowBytes(), x, y);

- 현재 캔버스에서 x, y 위치를 좌상단으로 info.width() * info.height()크기 만큼 데이터를 CPU 메모리로 복사.

파라메터 타입 설명
dstInfo const SkImageInfo& 복사할 대상 이미지의 정보 (크기, 포맷 등)
dstPixels void* 픽셀 데이터를 저장할 메모리 버퍼 포인터
dstRowBytes size_t 버퍼의 한 줄(row)당 바이트 수 (info.minRowBytes() 권장)
srcX, srcY int 캔버스에서 읽기 시작할 좌상단 좌표

 

SkImages::RasterFromData(..)

- 메모리 버퍼(SkData)를 기반으로 래스터 이미지(SkImage)를 생성하는 팩토리 함수

- 래스터라는 의미는 픽셀 값 하나하나를 메모리에 저장한 이미지를 의미

파라메터 타입 설명
info const SkImageInfo& 이미지의 크기, 포맷, 알파타입, 색공간 등을 포함하는 메타정보
data sk_sp<SkData> 이미지 픽셀 데이터가 들어있는 SkData 객체 (불변 or 읽기용 메모리)
rowBytes size_t 한 줄당 바이트 수 (stride). 일반적으로 info.minRowBytes() 사용

 

 

 

1번 2번 3번 4번 각 좌상단 시작좌표이고, width, height는 64, 64 이다.

readPixels는 항상 좌상단 (0, 0) 기준으로 w,h ( 64, 64 ) 범위에 gpu -> cpu 복사를 한다.

코드 상으로는 for 문에서 data->writable_data()를 4번 호출하여 매번 덮어쓰지만,

 

색칠된 영역(파랑 갈색,분홍,초록) 이외에는 64, 64 범위를 벗어나 버리기 때문에 0 으로 채워짐.

그렇기 때문에 read pixel 값이 덮어써지 않음!!

 


WritePixels

 

 

void draw(SkCanvas* canvas) {
    // 2x2 RGBA premultiplied CPU 메모리용 이미지 포맷 생성
    SkImageInfo imageInfo = SkImageInfo::MakeN32Premul(2, 2);

    SkBitmap bitmap;
    bitmap.setInfo(imageInfo);         // 비트맵에 이미지 포맷 설정
    uint32_t pixels[4];                // 2x2 픽셀 버퍼 (CPU 메모리)
    bitmap.setPixels(pixels);          // 외부 버퍼를 비트맵에 연결 (GPU 아님)

    // 2픽셀 간격으로 256x256 캔버스를 채움
    for (int y = 0; y < 256; y += 2) {
        for (int x = 0; x < 256;  x += 2) {
            // 픽셀 색상 지정 (비트 연산으로 다양한 패턴 생성)
            pixels[0] = SkColorSetRGB(x, y, x | y);              // 위치 기반 기본 색
            pixels[1] = SkColorSetRGB(x ^ y, y, x);              // 대각선 패턴 (XOR)
            pixels[2] = SkColorSetRGB(x, x & y, y);              // 공통 영역 강조 (AND)
            pixels[3] = SkColorSetRGB((~x) & 0xFF, (~y) & 0xFF, x); // 반전 색상 효과

            // 현재 pixels 내용을 (x, y)에 2x2 비트맵으로 복사 (write 시 GPU로 전송됨)
            canvas->writePixels(bitmap, x, y);
        }
    }
}

 

이 예제에서의 중요한 특징은 아래와 같다.

1) pixels[4] 배열에 RGBA 값을 CPU에 메모리에 업데이트

- MakeN32Premul 함수를 통해 (2, 2) SkImageInfo 생성

- SkBitmap을 생성 한 뒤, setInfo SkImageInfo하여 이미지 정보를 입력

 

2) x, y값 비트연산의 따른 효과

- x | y : x, y 비트 중 하나만 1이면 1이므로, 전반적으로 밝은 픽셀이 생성.

- x ^ y : x, y 비트가 다른 경우에 1이므로, 변화가 생기는 경계나 대각선에서 값이 튐 --> 체커보드 무늬

- x & y : x, y 비트 둘다 1이어야 1이므로, 특정 위치에서만 진해지는 픽셀 생김 (corner)

- (~x) & 0xff , 색의 반전, 어두운 곳은 밝게, 밝은 곳은 어둡게..

 

3) writePixels를 통하여 캔버스 렌더링 ( gpu 버퍼에 write !! )

- uint32_t pixels[4] 픽셀용 버퍼를 생성하여 bitmap.setPixels 설정.

- SkBitmap은 GPU 텍스처가 아니다, CPU 메모리 기반 이미지 컨테이너다.

 

 

 

 

728x90

'SKIA' 카테고리의 다른 글

2.7 SkSurface  (0) 2025.05.19
2.6 Canvas CheckerBoard  (0) 2025.05.13
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