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);
}
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 메모리 기반 이미지 컨테이너다.
'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 |