Welcome To

Schnee’s Blog!

크롬의 최대 스크린샷 크기 알아보기

문제

회사에서 전체 페이지를 스크린샷으로 이미지를 추출해야 하는 요구사항이 생겼다.

전체 페이지의 경우 스크린샷의 크기가 매우 매우 커질 수가 있다.

스크린샷이 커질 때 스크린샷을 해보니, 스크린샷이 짤리는 문제가 생겼다.

예시 이미지

원인

크롬 (크로미움) 브라우저의 최대 텍스쳐 사이즈는 16,384px이고, 그 이상의 사이즈는 짤리고 0px 부터 반복되어 캡쳐된다..

정말 그런지 볼까?

크롬 개발자의 답변에서 오픈한 코드로 힌트를 얻을 수 있다.

Answer

omg... 코드를 보려니 2017년의 답변이고 현재 7년이 지난만큼 코드도 바뀐 모양이다.

Error

다행히 폴더 구조를 완전히 바꾸지는 않은 모양이다. resource_pool.cc 파일에서 해당 정보에 대한 힌트를 얻을 수 있었다.

getArea

주석을 보니 GetArea 함수는 overflow (뷰포트가 오버플로우 한다는 의미로 추정된다) 된다면 에러를 발생시키는데, max_texture_size() 라는 함수로 이를 제한해서 overflow가 되지 않나보다.

그러면 이제 max_texture_size 에 대한 정보를 찾아보자!

아쉽게도 나는 C++과 OpenGL에 대한 지식이 제로여서 GPT의 도움을 받아서 찾아보았다.

GPT1

max_texture_size 에 대한 정보를 찾았다! Max Texture Size

0x0D33 이라는 값을 가지고 있는데… 이건 사실 십진수로 3379이다. 실제로 계산하는 값이라기 보다 무언가의 Key 인 것 같다.

GL_MAX_TEXTURE_SIZE 라는 변수를 사용하는 곳을 찾았다! GetIntegerv

GetIntegerv 라는 무언가를 사용하나보다. GetIntegerv 를 찾았다!

Chromium Code Search를 사용해보면서 느낀건데, 코드 텍스트 에디터를 참 잘만들었다.

사실상 웹으로 IDE를 쓰는 것처럼 검색하기가 편하다

검색한 변수가 구조체인지, 클래스인지, 함수인지 잘알려준다.

아무튼 코드를 이해할 수가 없어서 GPT한테 코드 내용을 물어봤다.

GPT2

정리하자면, GetIntegerv 를 호출할 때, GPU가 값을 조회하고 공유 메모리에 결과값을 올린다. (리턴하지 않는다라고 한다.)

아래와 같은 코드는 그래서 0x0D33 으로 값을 조회하고, &max_texture_size_ 라는 공유 메모리 주소에 결과값을 저장한다.

gl_->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size_);

그리고 &max_texture_size_ 주소를 조회해서 결과값을 사용하는 방식으로 보인다.

이 후는 하드웨어 (정확히는 GPU) 에 의존하여 결과값이 생성돼서 정확한 값을 추적하기 어렵다.

이로서 Eric Seckler의 아래 답변은 타당하다고 볼 수 있을 것 같다.

For the software GL backend we use with headless, this limit is 16384px.

문제를 해결하자

  1. puppeteer로 뷰포트를 옮긴다. 가장 일반적인 방법은 window.scrollTo()
  2. 뷰포트가 overflow 될 때까지 스크린샷한다.
  3. 여러 개의 스크린샷 이미지 값 Uint8Array 을 배열에 담는다.
  1. sharp로 위 이미지들을 합친다.
  2. 이미지 틀을 생성한다.
// 예시 코드 const baseImage = await sharp({ create: { width: 1000, height: 20000, }, }) .png() .toBuffer();
  1. 이미지 틀에 이미지 배열에 있는 이미지들을 합성한다.
// 예시 코드 const composites = []; for (let i = 0; i < screenshots.length; i++) { const imgBuffer = screenshots[i]; // 현재 스크린샷의 메타데이터(너비, 높이 등)를 얻어옴 const meta = await sharp(imgBuffer).metadata(); // 각 스크린샷의 y 좌표 (스크롤한 위치) let top = i * viewportHeight; // 만약 스크린샷이 이미지 틀을 벗어난다면 크롭 if (top + meta.height > totalHeight) { const cropHeight = totalHeight - top; // base image 안에 들어갈 높이 const croppedBuffer = await sharp(imgBuffer) .extract({ left: 0, top: 0, width: meta.width, height: cropHeight }) .toBuffer(); composites.push({ input: croppedBuffer, top: top, left: 0, }); } else { // 그대로 합성 composites.push({ input: imgBuffer, top: top, left: 0, }); } }

주의할 점: 합성한 이미지가 이미지 틀의 사이즈보다 크면 다음 에러가 난다.

[Error: Input image exceeds pixel limit]

결과 (예시 이미지)

예시 이미지

출처