캔버스와 함께 사용하기 위해 이미지를 디코딩하는 것은 사용자가 아바타를 맞춤설정하거나, 이미지를 자르거나, 사진을 확대하는 것 등 다양한 용도로 사용할 수 있습니다. 이미지 디코딩의 문제는 CPU 집약적일 수 있고 때로는 버벅거림이나 체커보드를 의미할 수 있다는 점입니다. Chrome 50(및 Firefox 42 이상)부터 다른 옵션(createImageBitmap()
)이 제공됩니다. 이를 사용하면 백그라운드에서 이미지를 디코딩하고 새로운 ImageBitmap
프리미티브에 액세스할 수 있습니다. 이 프리미티브는 <img>
요소, 다른 캔버스, 동영상과 같은 방식으로 캔버스에 그릴 수 있습니다.
createImageBitmap()으로 blob 그리기
fetch()
(또는 XHR)로 blob 이미지를 다운로드하여 캔버스에 그리려고 한다고 가정해 보겠습니다. createImageBitmap()
가 없으면 이미지 요소와 Blob URL을 만들어 이미지를 사용할 수 있는 형식으로 가져와야 합니다. 이를 통해 페인팅으로 훨씬 더 직접적인 경로를 얻을 수 있습니다.
fetch(url)
.then(response => response.blob())
.then(blob => createImageBitmap(blob))
.then(imageBitmap => ctx.drawImage(imageBitmap, 0, 0));
이 접근 방식은 IndexedDB에 blob으로 저장된 이미지에도 작동하므로 blob을 편리한 중간 형식으로 만들 수 있습니다. 이에 따라 Chrome 50에서는 캔버스 요소에서 .toBlob()
메서드도 지원합니다. 즉, 캔버스 요소에서 blob을 생성하는 등의 작업이 가능합니다.
웹 작업자에서 createImageBitmap() 사용
createImageBitmap()
의 가장 좋은 기능 중 하나는 작업자에서도 사용할 수 있다는 것입니다. 즉, 이제 어디서나 이미지를 디코딩할 수 있습니다. 디코딩할 이미지가 많고 필수가 아니라고 여기는 경우, 이미지 URL을 Web Worker로 전달하며, Web Worker는 시간이 지나면서 다운로드하고 디코딩합니다. 그런 다음 캔버스에 그리도록 기본 스레드로 다시 전송합니다.
이를 위한 코드는 다음과 같습니다.
// In the worker.
fetch(imageURL)
.then(response => response.blob())
.then(blob => createImageBitmap(blob))
.then(imageBitmap => {
// Transfer the imageBitmap back to main thread.
self.postMessage({ imageBitmap }, [imageBitmap]);
}, err => {
self.postMessage({ err });
});
// In the main thread.
worker.onmessage = (evt) => {
if (evt.data.err)
throw new Error(evt.data.err);
canvasContext.drawImage(evt.data.imageBitmap, 0, 0);
}
현재 기본 스레드에서 createImageBitmap()
를 호출하면 정확히 이 스레드에서 디코딩이 실행됩니다. 하지만 Chrome이 다른 스레드에서 자동으로 디코딩을 실행하도록 하여 기본 스레드 워크로드를 줄이는 데 도움이 되도록 할 계획입니다. 하지만 기본 스레드에서 디코딩을 수행하는 것은 자바스크립트, 스타일 계산, 레이아웃, 페인팅 또는 합성 같은 다른 필수 작업을 차단할 수 있는 집약적인 작업이기 때문에 주의해야 합니다.
도우미 라이브러리
작업을 좀 더 간단하게 하기 위해 작업자의 디코딩을 처리하고 디코딩된 이미지를 기본 스레드로 다시 전송하여 캔버스에 그리는 도우미 라이브러리를 만들었습니다. 물론 자유롭게 모델을 리버스 엔지니어링하여 앱에 적용해야 합니다. 주요 이점은 더 세밀한 제어이지만, 평소와 같이 더 많은 코드와 디버그해야 하며 <img>
요소를 사용하는 것보다 더 많은 특이 사례를 고려할 수 있습니다.
이미지 디코딩을 좀 더 세부적으로 제어해야 하는 경우 createImageBitmap()
를 사용하면 됩니다. Chrome 50에서 이 기능을 확인하고 사용해 보며 의견을 공유해 주세요.