Color
는 RGBA 색상을 정의합니다. 각 성분들은 $[0, 255]$ 범위를 가지기에, 하나의 Color
는 하나의 Uint32
로 표현할 수 있습니다. 이해하기 쉽게 C/C++ 의 Bit-field 와 Union 으로 표현하자면 아래와 같습니다:
union Color {
struct { // Warning: ISO C++ prohibits anonymous structs [-Wpedantic]
uint32_t a : 8; // 익명 구조체는 표준은 아니지만, 여기서는 편의를 위해 사용했습니다.
uint32_t b : 8;
uint32_t g : 8;
uint32_t r : 8;
};
uint32_t rgba;
};
sizeof(Color) == 4
이며, 각 성분들에 접근하기 위해서는 비트 연산자 >>>, <<, &, |
들을 사용해야 합니다. 주목할 점은 r
성분이 rgba
의 LSB (Least Significant Byte) 에 저장된다는 점입니다. 반대로 a
성분은 rgba
의 MSB (Most Significant Byte) 에 저장됩니다.
이는 rgba
가 항상 little endian 의 순서로 메모리에 저장된다고 가정했기 때문입니다. 덕분에 Uint32Array
에 Color.rgba
를 저장했을 때, LSB 에 해당하는 r
성분이 낮은 인덱스의 byte 에 저장되게 됩니다. Uint32Array
의 메모리를 Uint8Array
로 해석하여 본다면 아래와 같습니다:
const uint32Array = new Uint32Array(2);
const uint8Array = new Uint8Array(uint32Array.buffer);
uint32Array[0] = Color.yellow.rgba; // rgba(255, 234, 4, 255)
uint32Array[1] = Color.blue.rgba; // rgba(0, 0, 255, 255)
console.log(uint32Array); // Uint32Array(2) [4278512383, 4294901760, buffer: ArrayBuffer(8), byteLength: 8, byteOffset: 0, length: 2, Symbol(Symbol.toStringTag): 'Uint32Array']
console.log(uint8Array); // Uint8Array(8) [255, 234, 4, 255, 0, 0, 255, 255, buffer: ArrayBuffer(8), byteLength: 8, byteOffset: 0, length: 8, Symbol(Symbol.toStringTag): 'Uint8Array']
const imageData = new ImageData(new Uint8ClampedArray(uint8Array.buffer), 1);
const cvs = document.querySelector("#canvas");
const ctx = cvs.getContext('2d');
ctx.putImageData(imageData, 0, 0);
예제를 보면 Uint32Array
에 RGBA 색상을 저장할때 Uint32
를 한번 저장하기만 하면 됩니다. 이는Renderer.setPixel()
의 전체적인 효율을 향상시켜줍니다.
이후 Uint32Array
를 Uint8ClampedArray
로 해석하여 CanvasRenderingContext2D.putImageData()
에게 넘겨줍니다. 여기서 하나의 색상의 성분들은 r
⇒ g
⇒ b
⇒ a
순으로 저장되어 있어야 함에 주목하시길 바랍니다.
<aside>
스칼라 s 를 사용해 선형보간을 수행합니다. 결과는 $from\cdot(1-s) + to\cdot s$ 이며 out 에 담아 돌려줍니다.
</aside>
<aside>
자주 사용되는 RGBA 색상을 나타내는 Color 를 생성합니다. 예를 들어 Color.red
는 new Color(255, 0, 0, 255)
의 약칭(shorthand)입니다.
</aside>
<aside>
(r,g,b,a)를 나타내는 Color 를 생성합니다. 각 성분은 [0, 255] 까지의 범위를 가집니다.
</aside>