WebP 무손실 비트스트림 사양

지르키 알라쿠할라 박사 Google, Inc., 2023-03-09

개요

WebP 무손실은 ARGB 이미지의 무손실 압축을 위한 이미지 형식입니다. 무손실 형식은 완전 투명한 픽셀의 색상 값을 포함하여 픽셀 값을 정확하게 저장하고 복원합니다. 벌크 데이터 압축에는 순차 데이터 압축 (LZ77), 프리픽스 코딩, 색상 캐시를 위한 범용 알고리즘이 사용됩니다. PNG보다 빠른 디코딩 속도와 현재 PNG 형식을 사용할 때보다 25% 더 밀도 있는 압축률이 입증되었습니다.

1 서문

이 문서에서는 WebP 무손실 이미지의 압축된 데이터 표현을 설명합니다. 이 문서는 WebP 무손실 인코더 및 디코더 구현에 관한 자세한 참조로 사용됩니다.

이 문서에서는 C 프로그래밍 언어 구문을 광범위하게 사용하여 비트스트림을 설명하고 비트를 읽는 함수 ReadBits(n)가 있다고 가정합니다. 바이트는 자신을 포함하는 스트림의 자연스러운 순서로 읽히며, 각 바이트의 비트는 가장 작은 비트 우선 순서로 읽힙니다. 여러 비트를 동시에 읽으면 정수는 원래 데이터의 원래 순서로 구성됩니다. 반환된 정수의 최상위 비트는 원본 데이터의 최상위 비트이기도 합니다. 따라서

b = ReadBits(2);

아래의 두 문과 동일합니다.

b = ReadBits(1);
b |= ReadBits(1) << 1;

각 색상 구성요소, 즉 알파, 빨간색, 파란색, 녹색이 8비트 바이트를 사용하여 표시된다고 가정합니다. 상응하는 유형을 uint8로 정의합니다. 전체 ARGB 픽셀은 32비트로 구성된 부호 없는 정수인 uint32라는 유형으로 표현됩니다. 변환 동작을 보여주는 코드에서 이러한 값은 비트 31..24는 알파, 비트 23..16에서는 빨간색, 비트 15..8에서는 녹색, 비트 7..0에서는 파란색으로 코드화됩니다. 그러나 형식 구현은 내부적으로 다른 표현을 자유롭게 사용할 수 있습니다.

일반적으로 WebP 무손실 이미지에는 헤더 데이터, 변환 정보, 실제 이미지 데이터가 포함됩니다. 헤더에는 이미지의 너비와 높이가 포함됩니다. WebP 무손실 이미지는 엔트로피 인코딩되기 전에 네 가지 유형의 변환을 거칠 수 있습니다. 비트스트림의 변환 정보에는 각각의 역 변환을 적용하는 데 필요한 데이터가 포함됩니다.

2 명명법

ARGB : 계정 관리 플랫폼
알파, 빨간색, 녹색, 파란색 값으로 구성된 픽셀 값입니다.
ARGB 이미지
ARGB 픽셀을 포함하는 2차원 배열입니다.
색상 캐시
짧은 코드로 재현할 수 있도록 최근에 사용된 색상을 저장하는 작은 해시 주소 배열입니다.
색상 색인 생성 이미지
작은 정수를 사용하여 색인을 생성할 수 있는 색상의 1차원 이미지입니다(WebP 내에서 최대 256개 무손실).
이미지 색상 변환
색상 구성요소의 상관관계에 관한 데이터가 포함된 2차원 저해상도 이미지입니다.
거리 매핑
2차원 근접성에서 가장 작은 픽셀 값을 갖도록 LZ77 거리를 변경합니다.
엔트로피 이미지
이미지의 각 정사각형에서 어떤 엔트로피 코딩을 사용해야 하는지 나타내는 2차원 저해상도 이미지입니다. 즉, 각 픽셀은 메타 접두사 코드입니다.
LZ77
기호를 내보내거나 이전 기호의 시퀀스로 설명하는 사전 기반 슬라이딩 윈도우 압축 알고리즘입니다.
메타 접두어 코드
메타 접두사 테이블의 요소의 색인을 생성하는 작은 정수 (최대 16비트)입니다.
예측자 이미지
이미지의 특정 정사각형에 어떤 공간 예측자가 사용되는지 나타내는 2차원 저해상도 이미지입니다.
프리픽스 코드
더 자주 사용되는 코드에 적은 수의 비트가 사용되는 엔트로피 코딩의 일반적인 방법입니다.
프리픽스 코딩
큰 정수를 엔트로피 코딩하는 방법으로, 엔트로피 코드를 사용하여 정수의 몇 비트를 코딩하고 나머지 비트를 원시 코드로 코드화합니다. 따라서 기호의 범위가 클 때도 엔트로피 코드의 설명이 비교적 작게 유지될 수 있습니다.
스캔 줄 순서
왼쪽 상단 픽셀에서 시작하여 픽셀의 처리 순서 (왼쪽에서 오른쪽, 위에서 아래로)입니다. 행이 완료되면 다음 행의 왼쪽 열에서 계속 진행합니다.

3 RIFF 헤더

헤더의 시작 부분에는 RIFF 컨테이너가 있습니다. 다음 21바이트로 구성됩니다.

  1. 문자열 'RIFF'입니다.
  2. 청크 길이의 리틀 엔디언 32비트 값으로, RIFF 헤더에 의해 제어되는 청크의 전체 크기입니다. 일반적으로 페이로드 크기 (파일 크기에서 8바이트를 뺀 값, 'RIFF' 식별자 4바이트, 값 자체 저장 4바이트)와 같습니다.
  3. 문자열 'WEBP' (RIFF 컨테이너 이름)입니다.
  4. 문자열 'VP8L' (무손실 인코딩 이미지 데이터의 경우 FourCC)
  5. 무손실 스트림의 바이트 수에 관한 Little Endian 32비트 값입니다.
  6. 1바이트 서명 0x2f.

비트스트림의 처음 28비트는 이미지의 너비와 높이를 지정합니다. 너비와 높이는 다음과 같이 14비트 정수로 디코딩됩니다.

int image_width = ReadBits(14) + 1;
int image_height = ReadBits(14) + 1;

이미지 너비와 높이의 14비트 정밀도는 WebP 무손실 이미지의 최대 크기를 16,384x16,384픽셀로 제한합니다.

alpha_is_used 비트는 힌트일 뿐이며 디코딩에 영향을 미치지 않습니다. 그림에서 모든 알파 값이 255이면 0으로, 그렇지 않으면 1로 설정해야 합니다.

int alpha_is_used = ReadBits(1);

version_number는 0으로 설정해야 하는 3비트 코드입니다. 다른 값은 모두 오류로 취급해야 합니다.

int version_number = ReadBits(3);

4개 변환

변환은 공간 및 색상 상관관계를 모델링하여 나머지 기호 엔트로피를 줄일 수 있는 이미지 데이터의 가역적인 조작입니다. 최종 압축을 더 조밀하게 만들 수 있습니다.

이미지는 네 가지 유형의 변환을 거칠 수 있습니다. 1비트는 변환의 존재를 나타냅니다. 각 변환은 한 번만 사용할 수 있습니다. 변환은 기본 수준 ARGB 이미지에만 사용됩니다. 저해상도 이미지(색상 변환 이미지, 엔트로피 이미지, 예측자 이미지)에는 변환의 끝을 나타내는 0비트도 변환이 없습니다.

일반적으로 인코더는 이러한 변환을 사용하여 잔여 이미지에서 섀넌 엔트로피를 줄입니다. 또한 변환 데이터는 엔트로피 최소화에 따라 결정될 수 있습니다.

while (ReadBits(1)) {  // Transform present.
  // Decode transform type.
  enum TransformType transform_type = ReadBits(2);
  // Decode transform data.
  ...
}

// Decode actual image data (Section 5).

변환이 있는 경우 다음 두 비트는 변환 유형을 지정합니다. 변환에는 네 가지 유형이 있습니다.

enum TransformType {
  PREDICTOR_TRANSFORM             = 0,
  COLOR_TRANSFORM                 = 1,
  SUBTRACT_GREEN_TRANSFORM        = 2,
  COLOR_INDEXING_TRANSFORM        = 3,
};

변환 유형 뒤에는 변환 데이터가 옵니다. 변환 데이터에는 역 변환을 적용하는 데 필요한 정보가 포함되며 변환 유형에 따라 다릅니다. 역변환은 비트스트림에서 읽은 순서인 마지막 항목부터 역순으로 적용됩니다.

다음으로 다양한 유형의 변환 데이터를 설명합니다.

4.1 예측자 변환

예측자 변환은 인접 픽셀이 상관관계가 있는 경우가 많다는 사실을 이용하여 엔트로피를 줄일 수 있습니다. 예측자 변환에서 현재 픽셀 값은 이미 디코딩된 픽셀 (스캔 라인 순서)에서 예측되며 잔여 값 (실제 - 예측)만 인코딩됩니다. 픽셀의 녹색 구성요소는 ARGB 이미지의 특정 블록 내에서 사용되는 14개의 예측자 중 하나를 정의합니다. 예측 모드는 사용할 예측 유형을 결정합니다. 이미지를 정사각형으로 나누면 정사각형의 모든 픽셀이 동일한 예측 모드를 사용합니다.

예측 데이터의 처음 3비트는 블록 너비와 높이를 비트 수로 정의합니다.

int size_bits = ReadBits(3) + 2;
int block_width = (1 << size_bits);
int block_height = (1 << size_bits);
#define DIV_ROUND_UP(num, den) (((num) + (den) - 1) / (den))
int transform_width = DIV_ROUND_UP(image_width, 1 << size_bits);

변환 데이터에는 이미지의 각 블록에 대한 예측 모드가 포함됩니다. 이 이미지는 픽셀의 녹색 구성요소가 ARGB 이미지의 특정 블록 내에 있는 모든 block_width * block_height 픽셀에 사용되는 14개의 예측자 중 어떤 것을 정의하는지 정의하는 하위 해상도 이미지입니다. 이 하위 해상도 이미지는 5장에 설명된 것과 동일한 기법으로 인코딩됩니다.

블록 열의 수인 transform_width는 2차원 색인 생성에 사용됩니다. 픽셀 (x, y)의 경우 다음과 같은 방법으로 각 필터 블록 주소를 계산할 수 있습니다.

int block_index = (y >> size_bits) * transform_width +
                  (x >> size_bits);

14가지 예측 모드가 있습니다. 각 예측 모드에서 현재 픽셀 값은 값이 이미 알려진 하나 이상의 인접 픽셀에서 예측됩니다.

다음과 같이 현재 픽셀 (P)의 인접 픽셀 (TL, T, TR, L)을 선택했습니다.

O    O    O    O    O    O    O    O    O    O    O
O    O    O    O    O    O    O    O    O    O    O
O    O    O    O    TL   T    TR   O    O    O    O
O    O    O    O    L    P    X    X    X    X    X
X    X    X    X    X    X    X    X    X    X    X
X    X    X    X    X    X    X    X    X    X    X

여기서 TL은 왼쪽 상단을, T는 상단을 의미하고, TR은 오른쪽 상단을, L은 왼쪽을 의미합니다. P 값을 예측할 때 O, TL, T, TR, L 픽셀은 모두 이미 처리되었으며 P 픽셀과 모든 X 픽셀은 알 수 없습니다.

이전의 인접 픽셀을 고려하여 다양한 예측 모드는 다음과 같이 정의됩니다.

모드 현재 픽셀의 각 채널에 대한 예측 값
0 0xff000000 (ARGB로 검은색 단색을 나타냄)
1 L
2 T
3 터키 리라(TR)
4 TL
5 평균2(평균2(L, TR), T)
6 평균2(L, TL)
7 평균2(L, T)
8 평균2(TL, T)
9 평균2(T, TR)
10 평균2(평균2(L, TL), 평균2(T, TR))
11 선택(L, T, TL)
12 ClampAddSubtractFull(L, T, TL)
13 ClampAddSubtractHalf(Average2(L, T), TL)

Average2는 각 ARGB 구성요소에 관해 다음과 같이 정의됩니다.

uint8 Average2(uint8 a, uint8 b) {
  return (a + b) / 2;
}

예측자 선택은 다음과 같이 정의됩니다.

uint32 Select(uint32 L, uint32 T, uint32 TL) {
  // L = left pixel, T = top pixel, TL = top-left pixel.

  // ARGB component estimates for prediction.
  int pAlpha = ALPHA(L) + ALPHA(T) - ALPHA(TL);
  int pRed = RED(L) + RED(T) - RED(TL);
  int pGreen = GREEN(L) + GREEN(T) - GREEN(TL);
  int pBlue = BLUE(L) + BLUE(T) - BLUE(TL);

  // Manhattan distances to estimates for left and top pixels.
  int pL = abs(pAlpha - ALPHA(L)) + abs(pRed - RED(L)) +
           abs(pGreen - GREEN(L)) + abs(pBlue - BLUE(L));
  int pT = abs(pAlpha - ALPHA(T)) + abs(pRed - RED(T)) +
           abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));

  // Return either left or top, the one closer to the prediction.
  if (pL < pT) {
    return L;
  } else {
    return T;
  }
}

ClampAddSubtractFullClampAddSubtractHalf 함수는 각 ARGB 구성요소에 다음과 같이 실행됩니다.

// Clamp the input value between 0 and 255.
int Clamp(int a) {
  return (a < 0) ? 0 : (a > 255) ? 255 : a;
}
int ClampAddSubtractFull(int a, int b, int c) {
  return Clamp(a + b - c);
}
int ClampAddSubtractHalf(int a, int b) {
  return Clamp(a + (a - b) / 2);
}

일부 테두리 픽셀에는 특별한 처리 규칙이 있습니다. 예측 변환이 있는 경우 이러한 픽셀의 모드 [0..13] 과 관계없이 이미지의 맨 왼쪽 상단 픽셀의 예측 값은 0xff000000, 상단 행의 모든 픽셀은 L픽셀, 맨 왼쪽 열의 모든 픽셀은 T픽셀입니다.

맨 오른쪽 열에 있는 픽셀의 TR 픽셀을 처리하는 것은 훌륭합니다. 맨 오른쪽 열에 있는 픽셀은 테두리에 없는 픽셀과 마찬가지로 [0..13] 모드를 사용하여 예측되지만 현재 픽셀과 동일한 행에 있는 가장 왼쪽 픽셀이 대신 TR 픽셀로 사용됩니다.

최종 픽셀 값은 예측 값의 각 채널을 인코딩된 잔여 값에 더하여 얻습니다.

void PredictorTransformOutput(uint32 residual, uint32 pred,
                              uint8* alpha, uint8* red,
                              uint8* green, uint8* blue) {
  *alpha = ALPHA(residual) + ALPHA(pred);
  *red = RED(residual) + RED(pred);
  *green = GREEN(residual) + GREEN(pred);
  *blue = BLUE(residual) + BLUE(pred);
}

4.2 색상 변환

색상 변환의 목표는 각 픽셀의 R, G, B 값을 데코레이션하는 것입니다. 색상 변환은 녹색 (G) 값을 그대로 유지하고 녹색 값을 기준으로 빨간색 (R) 값을 변환하며 녹색 값을 기준으로 파란색 (B) 값을 변환한 다음 빨간색 값을 기준으로 변환합니다.

예측자 변환의 경우와 마찬가지로, 먼저 이미지가 블록으로 나뉘고 블록의 모든 픽셀에 동일한 변환 모드가 사용됩니다. 각 블록에는 세 가지 유형의 색상 변환 요소가 있습니다.

typedef struct {
  uint8 green_to_red;
  uint8 green_to_blue;
  uint8 red_to_blue;
} ColorTransformElement;

실제 색상 변환은 색상 변환 델타를 정의하여 실행됩니다. 색상 변환 델타는 특정 블록의 모든 픽셀에서 동일한 ColorTransformElement에 따라 달라집니다. 색상을 변환하는 동안 델타를 뺍니다. 그러면 역색상 변환은 단순히 이러한 델타를 추가하는 것입니다.

색상 변환 함수는 다음과 같이 정의됩니다.

void ColorTransform(uint8 red, uint8 blue, uint8 green,
                    ColorTransformElement *trans,
                    uint8 *new_red, uint8 *new_blue) {
  // Transformed values of red and blue components
  int tmp_red = red;
  int tmp_blue = blue;

  // Applying the transform is just subtracting the transform deltas
  tmp_red  -= ColorTransformDelta(trans->green_to_red,  green);
  tmp_blue -= ColorTransformDelta(trans->green_to_blue, green);
  tmp_blue -= ColorTransformDelta(trans->red_to_blue, red);

  *new_red = tmp_red & 0xff;
  *new_blue = tmp_blue & 0xff;
}

ColorTransformDelta는 3.5 고정 소수점 숫자 및 부호 있는 8비트 RGB 색상 채널 (c) [-128..127]을 나타내는 부호 있는 8비트 정수를 사용하여 계산되며 다음과 같이 정의됩니다.

int8 ColorTransformDelta(int8 t, int8 c) {
  return (t * c) >> 5;
}

ColorTransformDelta()를 호출하기 전에 8비트 부호 없는 표현 (uint8)에서 8비트 부호 있는 표현 (int8)로 변환해야 합니다. 부호 있는 값은 8비트 2의 보수 숫자로 해석되어야 합니다 (즉, uint8 범위 [128..255] 는 변환된 int8 값의 [-128..-1] 범위에 매핑됩니다).

곱셈은 더 많은 정밀도를 사용하여 이루어져야 합니다 (최소 16비트 정밀도). 여기서는 이동 작업의 부호 확장 속성이 중요하지 않습니다. 결과에서 가장 낮은 8비트만 사용되며 여기서 부호 확장 이동과 부호 없는 이동은 서로 일관됩니다.

이제 디코딩이 역색 변환을 적용하고 원래의 빨간색 및 파란색 값을 복구할 수 있도록 색상 변환 데이터의 콘텐츠를 설명합니다. 색상 변환 데이터의 처음 3비트에는 예측자 변환과 마찬가지로 이미지 블록의 너비와 높이가 비트 수로 포함됩니다.

int size_bits = ReadBits(3) + 2;
int block_width = 1 << size_bits;
int block_height = 1 << size_bits;

색상 변환 데이터의 나머지 부분에는 이미지의 각 블록에 해당하는 ColorTransformElement 인스턴스가 포함됩니다. 각 ColorTransformElement 'cte'는 알파 구성요소가 255, 빨간색 구성요소는 cte.red_to_blue, 녹색 구성요소는 cte.green_to_blue, 파란색 구성요소는 cte.green_to_red인 하위 해상도 이미지에서 픽셀로 처리됩니다.

디코딩 중에는 블록의 ColorTransformElement 인스턴스가 디코딩되고 픽셀의 ARGB 값에 역색 변환이 적용됩니다. 앞서 언급했듯이 이 역색 변환은 ColorTransformElement 값을 빨간색 및 파란색 채널에 추가하기만 합니다. 알파 채널과 녹색 채널은 그대로 유지됩니다.

void InverseTransform(uint8 red, uint8 green, uint8 blue,
                      ColorTransformElement *trans,
                      uint8 *new_red, uint8 *new_blue) {
  // Transformed values of red and blue components
  int tmp_red = red;
  int tmp_blue = blue;

  // Applying the inverse transform is just adding the
  // color transform deltas
  tmp_red  += ColorTransformDelta(trans->green_to_red, green);
  tmp_blue += ColorTransformDelta(trans->green_to_blue, green);
  tmp_blue +=
      ColorTransformDelta(trans->red_to_blue, tmp_red & 0xff);

  *new_red = tmp_red & 0xff;
  *new_blue = tmp_blue & 0xff;
}

4.3 녹색 빼기 변환

녹색 감산 변환은 각 픽셀의 빨간색 및 파란색 값에서 녹색 값을 뺍니다. 이 변환이 있으면 디코더는 빨간색 값과 파란색 값 모두에 녹색 값을 추가해야 합니다. 이 변환과 관련된 데이터는 없습니다. 디코더는 다음과 같이 역 변환을 적용합니다.

void AddGreenToBlueAndRed(uint8 green, uint8 *red, uint8 *blue) {
  *red  = (*red  + green) & 0xff;
  *blue = (*blue + green) & 0xff;
}

이 변환은 색상 변환을 사용하여 모델링할 수 있으므로 중복되지만 여기에는 추가 데이터가 없으므로 전체 입자 색상 변환보다 적은 비트를 사용하여 녹색 감산 변환을 코딩할 수 있습니다.

4.4 색상 색인 변환

고유한 픽셀 값이 많지 않은 경우 색상 색인 배열을 만들고 픽셀 값을 배열의 색인으로 대체하는 것이 더 효율적일 수 있습니다. 색상 색인 변환을 사용하면 됩니다. (WebP 무손실 맥락에서는 특히 팔레트 변환이라고 부르지 않습니다. WebP 무손실 인코딩에는 색상 캐시와 유사하지만 더 동적인 개념이 존재하기 때문입니다.)

색상 색인 생성 변환은 이미지의 고유한 ARGB 값 수를 확인합니다. 이 숫자가 기준점 (256) 미만이면 ARGB 값의 배열을 만든 다음 픽셀 값을 상응하는 색인으로 바꾸는 데 사용됩니다. 픽셀의 녹색 채널이 색인으로 대체되고 모든 알파 값은 255로 설정되며 모든 빨간색 및 파란색 값은 0으로 설정됩니다.

변환 데이터에는 색상 표 크기와 색상 표의 항목이 포함됩니다. 디코더는 다음과 같이 색상 색인 생성 변환 데이터를 읽습니다.

// 8-bit value for the color table size
int color_table_size = ReadBits(8) + 1;

색상표는 이미지 저장 형식 자체를 사용하여 저장됩니다. 높이 1픽셀, 너비 color_table_size라고 가정하고 RIFF 헤더, 이미지 크기, 변환 없이 이미지를 읽어 색상표를 얻을 수 있습니다. 색상표는 이미지 엔트로피를 줄이기 위해 항상 뺄셈 코딩됩니다. 팔레트 색상의 델타는 일반적으로 색상 자체보다 훨씬 적은 엔트로피를 포함하므로 작은 이미지의 경우 상당한 절감 효과를 얻게 됩니다. 디코딩에서 각 ARGB 구성요소별로 이전 색상 구성요소 값을 별도로 추가하고 결과의 가장 중요하지 않은 8비트를 저장하여 색상 표의 모든 최종 색상을 얻을 수 있습니다.

이미지의 역 변환은 단순히 픽셀 값 (색상 표의 색인)을 실제 색상 표 값으로 바꿉니다. 색인 생성은 ARGB 색상의 녹색 구성요소를 기반으로 합니다.

// Inverse transform
argb = color_table[GREEN(argb)];

색인이 color_table_size 이상이면 argb 색상 값을 0x00000000 (투명 검은색)으로 설정해야 합니다.

색상표가 작은 경우 (16색 이하) 여러 픽셀이 단일 픽셀로 묶입니다. 픽셀 번들은 여러 픽셀 (2, 4, 8)을 단일 픽셀로 패킹하여 각각 이미지 너비를 줄입니다. 픽셀 번들링은 인접한 픽셀의 공동 분포 엔트로피 코딩을 더 효율적으로 할 수 있고 산술 코딩과 같은 이점을 엔트로피 코드에 부여하지만, 고유한 값이 16개 이하인 경우에만 사용할 수 있습니다.

color_table_size는 결합되는 픽셀 수를 지정합니다.

int width_bits;
if (color_table_size <= 2) {
  width_bits = 3;
} else if (color_table_size <= 4) {
  width_bits = 2;
} else if (color_table_size <= 16) {
  width_bits = 1;
} else {
  width_bits = 0;
}

width_bits의 값은 0, 1, 2 또는 3입니다. 값이 0이면 이미지에 대해 픽셀 번들링이 실행되지 않음을 나타냅니다. 값이 1인 경우 두 픽셀이 결합되어 있으며 각 픽셀의 범위가 [0..15]임을 나타냅니다. 값이 2이면 4개의 픽셀이 결합되었으며 각 픽셀의 범위가 [0..3]임을 나타냅니다. 값이 3이면 8개의 픽셀이 결합되어 있으며 각 픽셀의 범위가 [0..1], 즉 이진 값임을 나타냅니다.

값은 다음과 같이 녹색 구성요소에 포함됩니다.

  • width_bits = 1: x 값마다 x 사람이 0 (mod 2)인 경우 x의 녹색 값이 x / 2에서 녹색 값의 최하위 4개 비트에 배치되고 x + 1의 녹색 값이 x / 2에서 녹색 값의 최상위 비트 4개에 배치됩니다.
  • width_bits = 2: x 값마다(x 사람이 0(mod 4)인 경우) x의 녹색 값은 x / 4에서 녹색 값의 최하위 2개 비트에 배치되고 x + 1 ~ x + 3의 녹색 값은 x / 4에서 녹색 값의 상위 비트 순으로 배치됩니다.
  • width_bits = 3: x 값마다 x 사람이 0 (mod 8)이면 x의 녹색 값이 x / 8에서는 녹색 값의 최하위 비트에 배치되고 x+1에서 x+7까지 녹색 값의 최하위 비트에 x / 8에 있는 녹색 값의 상위 비트에 배치됩니다.

이 변환을 읽은 후에는 image_widthwidth_bits에 의해 서브 샘플링됩니다. 이는 후속 변환의 크기에 영향을 미칩니다. 새 크기는 앞에 정의된 대로 DIV_ROUND_UP를 사용하여 계산할 수 있습니다.

image_width = DIV_ROUND_UP(image_width, 1 << width_bits);

5 이미지 데이터

이미지 데이터는 스캔 라인 순서로 지정된 픽셀 값의 배열입니다.

5.1 이미지 데이터의 역할

Google은 5가지 역할로 이미지 데이터를 사용합니다.

  1. ARGB 이미지: 이미지의 실제 픽셀을 저장합니다.
  2. 엔트로피 이미지: 메타 접두사 코드를 저장합니다 ('메타 접두사 코드 디코딩' 참고).
  3. 예측자 이미지: 예측자 변환을 위한 메타데이터를 저장합니다 ('예측자 변환' 참조).
  4. 색상 변환 이미지: 이미지의 여러 블록에 관한 ColorTransformElement 값('색상 변환'에 정의됨)으로 생성됩니다.
  5. 색상 색인 생성 이미지: 색상 색인 생성 변환 ('색상 색인 생성 변환' 참고)을 저장하는 color_table_size 크기의 배열 (최대 256 ARGB 값)입니다.

5.2 이미지 데이터 인코딩

이미지 데이터의 인코딩은 역할과 무관합니다.

이미지는 먼저 일련의 고정 크기 블록 (일반적으로 16x16 블록)으로 나뉩니다. 이러한 각 블록은 자체 엔트로피 코드를 사용하여 모델링됩니다. 또한 여러 블록이 동일한 엔트로피 코드를 공유할 수 있습니다.

사유: 엔트로피 코드를 저장하면 비용이 발생합니다. 통계적으로 유사한 블록이 엔트로피 코드를 공유하여 해당 코드를 한 번만 저장하면 이 비용을 최소화할 수 있습니다. 예를 들어 인코더는 통계 속성을 사용하여 클러스터링하거나, 이미지를 인코딩하는 데 필요한 전체 비트 수를 줄일 때 무작위로 선택된 클러스터 쌍을 반복적으로 조인하여 비슷한 블록을 찾을 수 있습니다.

각 픽셀은 다음과 같이 가능한 세 가지 방법 중 하나를 사용하여 인코딩됩니다.

  1. 프리픽스로 코딩된 리터럴: 각 채널 (녹색, 빨간색, 파란색, 알파)은 독립적으로 엔트로피 코딩됩니다.
  2. LZ77 역참조: 픽셀의 시퀀스가 이미지의 다른 위치에서 복사됩니다.
  3. 색상 캐시 코드: 최근에 본 색상의 짧은 곱셈 해시 코드 (색상 캐시 색인)를 사용합니다.

다음 하위 섹션에서는 각각을 자세히 설명합니다.

5.2.1 프리픽스 코딩 리터럴

픽셀은 프리픽스로 코딩된 값인 녹색, 빨간색, 파란색, 알파 (순서대로)로 저장됩니다. 자세한 내용은 섹션 6.2.3을 참고하세요.

5.2.2 LZ77 역방향 참조

역방향 참조는 길이거리 코드의 튜플입니다.

  • 길이는 스캔 줄 순서에서 복사할 픽셀 수를 나타냅니다.
  • 거리 코드는 이전에 본 픽셀의 위치를 나타내는 숫자이며, 이 위치로부터 픽셀을 복사합니다. 정확한 매핑은 아래에 설명되어 있습니다.

길이 및 거리 값은 LZ77 프리픽스 코딩을 사용하여 저장됩니다.

LZ77 프리픽스 코딩은 큰 정수 값을 접두사 코드추가 비트의 두 부분으로 나눕니다. 접두사 코드는 엔트로피 코드를 사용하여 저장되고, 추가 비트는 엔트로피 코드 없이 있는 그대로 저장됩니다.

사유: 이 접근 방식을 사용하면 엔트로피 코드의 저장소 요구사항이 줄어듭니다. 또한 큰 값은 일반적으로 드물기 때문에 이미지의 매우 적은 값에 추가 비트가 사용됩니다. 따라서 이 접근 방식을 사용하면 전반적인 압축이 개선됩니다.

다음 표에는 다양한 값 범위를 저장하는 데 사용되는 접두사 코드와 추가 비트가 나와 있습니다.

값 범위 프리픽스 코드 추가 비트
1 0 0
2 1 0
3 2 0
4 3 0
5..6 4 1
7..8 5 1
9.12 6 2
13..16 7 2
... ... ...
3072..4096 23 10
... ... ...
524289..786432 38 18
786433..1048576 39 18

접두사 코드에서 (길이 또는 거리) 값을 가져오는 의사코드는 다음과 같습니다.

if (prefix_code < 4) {
  return prefix_code + 1;
}
int extra_bits = (prefix_code - 2) >> 1;
int offset = (2 + (prefix_code & 1)) << extra_bits;
return offset + ReadBits(extra_bits) + 1;
거리 매핑

앞서 언급했듯이 거리 코드는 이전에 본 픽셀의 위치를 나타내는 숫자이며, 이 위치로부터 픽셀을 복사합니다. 이 하위 섹션은 거리 코드와 이전 픽셀의 위치 간의 매핑을 정의합니다.

120보다 큰 거리 코드는 픽셀 거리를 스캔 선 순서로 나타내며 120만큼 오프셋됩니다.

최소 거리 코드 [1..120] 은 특별하며 현재 픽셀의 가까운 이웃용으로 예약됩니다. 이 지역은 다음과 같이 120픽셀로 구성됩니다.

  • 현재 픽셀에서 1~7행 위에 있고 현재 픽셀에서 최대 8열, 오른쪽으로 최대 7열인 픽셀입니다. [이러한 픽셀의 총 개수 = 7 * (8 + 1 + 7) = 112].
  • 현재 픽셀과 같은 행에 있고 현재 픽셀의 왼쪽에 최대 8개 열인 픽셀입니다. [이러한 픽셀 8개].

거리 코드 distance_code와 인접 픽셀 오프셋 (xi, yi) 간의 매핑은 다음과 같습니다.

(0, 1),  (1, 0),  (1, 1),  (-1, 1), (0, 2),  (2, 0),  (1, 2),
(-1, 2), (2, 1),  (-2, 1), (2, 2),  (-2, 2), (0, 3),  (3, 0),
(1, 3),  (-1, 3), (3, 1),  (-3, 1), (2, 3),  (-2, 3), (3, 2),
(-3, 2), (0, 4),  (4, 0),  (1, 4),  (-1, 4), (4, 1),  (-4, 1),
(3, 3),  (-3, 3), (2, 4),  (-2, 4), (4, 2),  (-4, 2), (0, 5),
(3, 4),  (-3, 4), (4, 3),  (-4, 3), (5, 0),  (1, 5),  (-1, 5),
(5, 1),  (-5, 1), (2, 5),  (-2, 5), (5, 2),  (-5, 2), (4, 4),
(-4, 4), (3, 5),  (-3, 5), (5, 3),  (-5, 3), (0, 6),  (6, 0),
(1, 6),  (-1, 6), (6, 1),  (-6, 1), (2, 6),  (-2, 6), (6, 2),
(-6, 2), (4, 5),  (-4, 5), (5, 4),  (-5, 4), (3, 6),  (-3, 6),
(6, 3),  (-6, 3), (0, 7),  (7, 0),  (1, 7),  (-1, 7), (5, 5),
(-5, 5), (7, 1),  (-7, 1), (4, 6),  (-4, 6), (6, 4),  (-6, 4),
(2, 7),  (-2, 7), (7, 2),  (-7, 2), (3, 7),  (-3, 7), (7, 3),
(-7, 3), (5, 6),  (-5, 6), (6, 5),  (-6, 5), (8, 0),  (4, 7),
(-4, 7), (7, 4),  (-7, 4), (8, 1),  (8, 2),  (6, 6),  (-6, 6),
(8, 3),  (5, 7),  (-5, 7), (7, 5),  (-7, 5), (8, 4),  (6, 7),
(-6, 7), (7, 6),  (-7, 6), (8, 5),  (7, 7),  (-7, 7), (8, 6),
(8, 7)

예를 들어 거리 코드 1는 주변 픽셀, 즉 현재 픽셀 위의 픽셀 (X 방향에서는 0픽셀 차이, Y 방향에서는 1픽셀 차이)의 오프셋 (0, 1)을 나타냅니다. 마찬가지로 거리 코드 3는 왼쪽 상단 픽셀을 나타냅니다.

디코더는 다음과 같이 거리 코드 distance_code를 스캔 라인 순서 거리 dist로 변환할 수 있습니다.

(xi, yi) = distance_map[distance_code - 1]
dist = xi + yi * image_width
if (dist < 1) {
  dist = 1
}

여기서 distance_map는 위에서 언급한 매핑이고 image_width은 픽셀 단위의 이미지 너비입니다.

5.2.3 색상 캐시 코딩

색상 캐시는 이미지에서 최근에 사용된 색상 집합을 저장합니다.

사유: 이렇게 하면 최근에 사용된 색상이 다른 두 가지 방법 (5.2.15.2.2에 설명됨)을 사용하여 이를 방출하는 것보다 더 효율적으로 참조할 수 있습니다.

색상 캐시 코드는 다음과 같이 저장됩니다. 첫째, 색상 캐시가 사용되었는지 나타내는 1비트 값이 있습니다. 이 비트가 0이면 색상 캐시 코드가 없으며 녹색 기호와 길이 접두사 코드를 디코딩하는 접두사 코드로 전송되지 않습니다. 그러나 이 비트가 1이면 색상 캐시 크기를 다음에 읽습니다.

int color_cache_code_bits = ReadBits(4);
int color_cache_size = 1 << color_cache_code_bits;

color_cache_code_bits는 색상 캐시 (1 << color_cache_code_bits)의 크기를 정의합니다. color_cache_code_bits에 허용되는 값 범위는 [1..11]입니다. 규정을 준수하는 디코더는 다른 값의 손상된 비트스트림을 나타내야 합니다.

색상 캐시는 크기 color_cache_size의 배열입니다. 각 항목은 하나의 ARGB 색상을 저장합니다. 색상은 (0x1e35a7bd * color) >> (32 - color_cache_code_bits)로 색인을 생성하여 조회됩니다. 색상 캐시에서는 조회가 한 번만 실행되며 충돌 해결은 없습니다.

이미지 디코딩 또는 인코딩을 시작할 때 모든 색상 캐시 값의 모든 항목이 0으로 설정됩니다. 색상 캐시 코드는 디코딩 시간에 이 색상으로 변환됩니다. 색상 캐시의 상태는 역참조를 통해 생성되었든 리터럴로 생성되었든 스트림에 나타나는 순서대로 모든 픽셀을 캐시에 삽입하여 유지됩니다.

6 엔트로피 코드

6.1 개요

데이터는 대부분 표준 접두어 코드를 사용하여 코딩됩니다. 따라서 코드는 실제 프리픽스 코드가 아닌 프리픽스 코드 길이를 전송하여 전송됩니다.

특히 이 형식은 공간 변형 접두사 코딩을 사용합니다. 즉, 이미지의 여러 블록에서 다른 엔트로피 코드를 사용할 수 있습니다.

사유: 이미지 영역마다 특성이 다를 수 있습니다. 따라서 다른 엔트로피 코드를 사용할 수 있게 하면 유연성이 높아지고 압축이 향상될 수 있습니다.

6.2 세부정보

인코딩된 이미지 데이터는 여러 부분으로 구성됩니다.

  1. 접두어 코드 디코딩 및 빌드
  2. 메타 접두어 코드
  3. 엔트로피로 코딩된 이미지 데이터입니다.

특정 픽셀 (x, y)에는 픽셀과 연결된 5개의 접두어 코드 집합이 있습니다. 코드는 다음과 같습니다 (비트스트림 순서).

  • 접두사 코드 #1: 녹색 채널, 역참조 길이, 색상 캐시에 사용됩니다.
  • 접두사 코드 #2, #3, #4: 각각 빨간색, 파란색, 알파 채널에 사용됩니다.
  • 접두사 코드 #5: 역참조 거리에 사용됩니다.

지금부터 이 집합을 프리픽스 코드 그룹이라고 합니다.

6.2.1 프리픽스 코드 디코딩 및 빌드

이 섹션에서는 비트스트림에서 접두어 코드 길이를 읽는 방법을 설명합니다.

프리픽스 코드 길이는 두 가지 방법으로 코딩할 수 있습니다. 사용되는 메서드는 1비트 값으로 지정됩니다.

  • 이 비트가 1이면 단순 코드 길이 코드입니다.
  • 이 비트가 0이면 일반 코드 길이 코드입니다.

두 경우 모두 스트림의 일부인 사용되지 않은 코드 길이가 있을 수 있습니다. 비효율적일 수 있지만 형식에서는 허용됩니다. 설명된 트리는 완전한 바이너리 트리여야 합니다. 단일 리프 노드는 완전한 바이너리 트리로 간주되며 간단한 코드 길이 코드 또는 일반 코드 길이 코드를 사용하여 인코딩할 수 있습니다. 일반 코드 길이 코드를 사용하여 단일 리프 노드를 코딩하는 경우, 한 개를 제외한 모든 코드 길이는 0이고 단일 리프 노드 값은 길이가 1로 표시됩니다. 단일 리프 노드 트리를 사용할 때 비트가 사용되지 않더라도 마찬가지입니다.

단순 코드 길이 코드

이 변형은 프리픽스 기호 1개 또는 2개만 코드 길이가 1인 [0..255] 범위 내에 있는 특수한 경우에 사용됩니다. 다른 모든 프리픽스 코드 길이는 암시적으로 0입니다.

첫 번째 비트는 기호 수를 나타냅니다.

int num_symbols = ReadBits(1) + 1;

다음은 기호 값입니다.

이 첫 번째 기호는 is_first_8bits 값에 따라 1비트 또는 8비트를 사용하여 코딩됩니다. 범위는 각각 [0..1] 또는 [0..255]입니다. 두 번째 기호(있는 경우)는 항상 [0..255] 범위 내에 있고 8비트를 사용하여 코딩된 것으로 간주됩니다.

int is_first_8bits = ReadBits(1);
symbol0 = ReadBits(1 + 7 * is_first_8bits);
code_lengths[symbol0] = 1;
if (num_symbols == 2) {
  symbol1 = ReadBits(8);
  code_lengths[symbol1] = 1;
}

두 기호가 달라야 합니다. 중복 기호는 허용되지만 비효율적입니다.

참고: 또 다른 특수한 경우는 모든 프리픽스 코드 길이가 0 (빈 프리픽스 코드)인 경우입니다. 예를 들어 역참조가 없으면 거리의 접두사 코드는 비어 있을 수 있습니다. 마찬가지로 알파, 빨간색, 파란색의 접두어 코드는 동일한 메타 접두어 코드 내의 모든 픽셀이 색상 캐시를 사용하여 생성된 경우 비어 있을 수 있습니다. 그러나 이 경우에는 특별한 처리가 필요하지 않습니다. 빈 프리픽스 코드를 단일 기호 0가 포함된 프리픽스 코드로 코딩할 수 있기 때문입니다.

일반 코드 길이 코드

접두사 코드의 코드 길이는 8비트이며 다음과 같이 읽습니다. 먼저 num_code_lengths는 코드 길이의 수를 지정합니다.

int num_code_lengths = 4 + ReadBits(4);

코드 길이는 그 자체가 접두사 코드를 사용하여 인코딩됩니다. 하위 수준의 코드 길이인 code_length_code_lengths를 먼저 읽어야 합니다. 이러한 code_length_code_lengths의 나머지 (kCodeLengthCodeOrder의 순서에 따라)는 0입니다.

int kCodeLengthCodes = 19;
int kCodeLengthCodeOrder[kCodeLengthCodes] = {
  17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
};
int code_length_code_lengths[kCodeLengthCodes] = { 0 };  // All zeros
for (i = 0; i < num_code_lengths; ++i) {
  code_length_code_lengths[kCodeLengthCodeOrder[i]] = ReadBits(3);
}

다음으로 ReadBits(1) == 0인 경우 각 기호 유형(A, R, G, B, 거리)에 관한 다양한 읽기 기호의 최대 개수 (max_symbol)가 알파벳 크기로 설정됩니다.

  • G 채널: 256 + 24 + color_cache_size
  • 기타 리터럴 (A, R, B): 256
  • 거리 코드: 40

그렇지 않으면 다음과 같이 정의됩니다.

int length_nbits = 2 + 2 * ReadBits(3);
int max_symbol = 2 + ReadBits(length_nbits);

max_symbol가 기호 유형의 알파벳 크기보다 크면 비트스트림이 유효하지 않습니다.

그런 다음 프리픽스 테이블이 code_length_code_lengths에서 빌드되어 최대 max_symbol개의 코드 길이를 읽는 데 사용됩니다.

  • 코드 [0..15] 는 리터럴 코드 길이를 나타냅니다.
    • 값 0은 코딩된 기호가 없음을 의미합니다.
    • 값 [1..15] 는 각 코드의 비트 길이를 나타냅니다.
  • 코드 16은 0이 아닌 이전의 값 [3..6] 회, 즉 3 + ReadBits(2)회를 반복합니다. 0이 아닌 값을 내보내기 전에 코드 16을 사용하면 값 8이 반복됩니다.
  • 코드 17은 0의 연속 길이 [3..10], 즉 3 + ReadBits(3)회를 내보냅니다.
  • 코드 18은 길이가 0인 [11..138], 즉 11 + ReadBits(7)회 연속으로 방출됩니다.

코드 길이가 판독되면 각 기호 유형 (A, R, G, B, 거리)의 접두사 코드가 각 알파벳 크기를 사용하여 형성됩니다.

일반 코드 길이 코드는 전체 결정 트리를 코딩해야 합니다. 즉, 0이 아닌 모든 코드에 관한 2 ^ (-length)의 합계는 정확히 1이어야 합니다. 그러나 이 규칙에는 한 가지 예외가 있습니다. 단, 단일 리프 노드 트리에서는 리프 노드 값이 값 1로 표시되고 다른 값은 0으로 표시됩니다.

6.2.2 메타 접두사 코드 디코딩

앞서 언급했듯이 이 형식을 사용하면 이미지의 여러 블록에 서로 다른 프리픽스 코드를 사용할 수 있습니다. 메타 프리픽스 코드는 이미지의 여러 부분에 사용할 프리픽스 코드를 식별하는 색인입니다.

메타 프리픽스 코드는 이미지가 ARGB 이미지역할에서 사용 중인 경우에만 사용할 수 있습니다.

1비트 값으로 표시되는 메타 접두사 코드에는 두 가지 가능성이 있습니다.

  • 이 비트가 0이면 이미지의 모든 곳에서 사용되는 메타 프리픽스 코드 하나만 사용됩니다. 더 이상 데이터가 저장되지 않습니다.
  • 이 비트가 1이면 이미지는 여러 메타 접두사 코드를 사용합니다. 이러한 메타 접두사 코드는 엔트로피 이미지 (아래에 설명됨)로 저장됩니다.

픽셀의 빨간색과 녹색 구성요소는 ARGB 이미지의 특정 블록에 사용되는 16비트 메타 접두어 코드를 정의합니다.

엔트로피 이미지

엔트로피 이미지는 이미지의 여러 부분에서 사용되는 접두어 코드를 정의합니다.

처음 3비트는 prefix_bits 값을 포함합니다. 엔트로피 이미지의 크기는 prefix_bits에서 파생됩니다.

int prefix_bits = ReadBits(3) + 2;
int prefix_image_width =
    DIV_ROUND_UP(image_width, 1 << prefix_bits);
int prefix_image_height =
    DIV_ROUND_UP(image_height, 1 << prefix_bits);

여기서 DIV_ROUND_UP앞에서 정의된 대로 합니다.

다음 비트는 너비가 prefix_image_width이고 높이가 prefix_image_height인 엔트로피 이미지를 포함합니다.

메타 접두사 코드 해석

ARGB 이미지의 접두사 코드 그룹 수는 엔트로피 이미지에서 가장 큰 메타 접두사 코드를 찾아 얻을 수 있습니다.

int num_prefix_groups = max(entropy image) + 1;

여기서 max(entropy image)는 엔트로피 이미지에 저장된 가장 큰 프리픽스 코드를 나타냅니다.

각 프리픽스 코드 그룹에는 프리픽스 코드 5개가 포함되어 있으므로 프리픽스 코드의 총 개수는 다음과 같습니다.

int num_prefix_codes = 5 * num_prefix_groups;

ARGB 이미지의 픽셀 (x, y)이 주어지면 다음과 같이 사용할 접두사 코드를 가져올 수 있습니다.

int position =
    (y >> prefix_bits) * prefix_image_width + (x >> prefix_bits);
int meta_prefix_code = (entropy_image[position] >> 8) & 0xffff;
PrefixCodeGroup prefix_group = prefix_code_groups[meta_prefix_code];

여기서는 5개의 프리픽스 코드 집합을 나타내는 PrefixCodeGroup 구조의 존재를 가정합니다. 또한 prefix_code_groupsPrefixCodeGroup (크기가 num_prefix_groups)인 배열입니다.

그런 다음 '엔트로피로 코딩된 이미지 데이터 디코딩'에 설명된 대로 디코더는 접두사 코드 그룹 prefix_group를 사용하여 픽셀(x, y)을 디코딩합니다.

6.2.3 엔트로피로 코딩된 이미지 데이터 디코딩

이미지의 현재 위치 (x, y)에 대해 디코더는 먼저 상응하는 접두사 코드 그룹을 식별합니다 (이전 섹션에서 설명). 접두사 코드 그룹이 주어지면 픽셀은 다음과 같이 판독 및 디코딩됩니다.

그런 다음 접두사 코드 #1을 사용하여 비트스트림에서 기호 S를 읽습니다. S는 0에서 (256 + 24 + color_cache_size- 1) 사이의 정수입니다.

S의 해석은 값에 따라 다릅니다.

  1. S가 256 미만인 경우
    1. S를 녹색 구성요소로 사용합니다.
    2. 접두사 코드 #2를 사용하여 비트스트림에서 빨간색을 읽습니다.
    3. 프리픽스 코드 3을 사용하여 비트스트림에서 파란색을 읽습니다.
    4. 접두사 코드 #4를 사용하여 비트스트림에서 알파를 읽습니다.
  2. S가 256 이상이고 S가 256 + 24 미만인 경우
    1. 길이 접두사 코드로 S - 256을 사용합니다.
    2. 비트스트림에서 길이의 추가 비트를 읽습니다.
    3. 길이 프리픽스 코드와 읽은 추가 비트에서 역참조 길이 L을 결정합니다.
    4. 접두어 코드 #5를 사용하여 비트스트림에서 거리 접두어 코드를 읽습니다.
    5. 비트스트림에서의 거리에 대한 추가 비트를 읽습니다.
    6. 거리 접두사 코드와 읽은 추가 비트에서 역참조 거리 D를 구합니다.
    7. 현재 위치에서 시작하여 D 픽셀을 뺀 일련의 픽셀에서 L 픽셀 (스캔 행 순서)을 복사합니다.
  3. S가 256 + 24 이상인 경우
    1. S - (256 + 24)를 색상 캐시의 색인으로 사용합니다.
    2. 해당 인덱스의 색상 캐시에서 ARGB 색상을 가져옵니다.

7 형식의 전반적인 구조

다음은 ABNF (증강 배커스 나우어 형식) RFC 5234 RFC 7405 형식을 보여줍니다. 모든 세부정보를 다루지는 않습니다. 이미지의 끝 (EOI)은 암시적으로 픽셀 수 (image_width * image_height)로만 코딩됩니다.

*elementelement를 0회 이상 반복할 수 있음을 의미합니다. 5elementelement가 정확히 5번 반복됨을 의미합니다. %b는 바이너리 값을 나타냅니다.

7.1 기본 구조

format        = RIFF-header image-header image-stream
RIFF-header   = %s"RIFF" 4OCTET %s"WEBPVP8L" 4OCTET
image-header  = %x2F image-size alpha-is-used version
image-size    = 14BIT 14BIT ; width - 1, height - 1
alpha-is-used = 1BIT
version       = 3BIT ; 0
image-stream  = optional-transform spatially-coded-image

7.2 변환의 구조

optional-transform   =  (%b1 transform optional-transform) / %b0
transform            =  predictor-tx / color-tx / subtract-green-tx
transform            =/ color-indexing-tx

predictor-tx         =  %b00 predictor-image
predictor-image      =  3BIT ; sub-pixel code
                        entropy-coded-image

color-tx             =  %b01 color-image
color-image          =  3BIT ; sub-pixel code
                        entropy-coded-image

subtract-green-tx    =  %b10

color-indexing-tx    =  %b11 color-indexing-image
color-indexing-image =  8BIT ; color count
                        entropy-coded-image

7.3 이미지 데이터의 구조

spatially-coded-image =  color-cache-info meta-prefix data
entropy-coded-image   =  color-cache-info data

color-cache-info      =  %b0
color-cache-info      =/ (%b1 4BIT) ; 1 followed by color cache size

meta-prefix           =  %b0 / (%b1 entropy-image)

data                  =  prefix-codes lz77-coded-image
entropy-image         =  3BIT ; subsample value
                         entropy-coded-image

prefix-codes          =  prefix-code-group *prefix-codes
prefix-code-group     =
    5prefix-code ; See "Interpretation of Meta Prefix Codes" to
                 ; understand what each of these five prefix
                 ; codes are for.

prefix-code           =  simple-prefix-code / normal-prefix-code
simple-prefix-code    =  ; see "Simple Code Length Code" for details
normal-prefix-code    =  ; see "Normal Code Length Code" for details

lz77-coded-image      =
    *((argb-pixel / lz77-copy / color-cache-code) lz77-coded-image)

다음은 가능한 시퀀스의 예입니다.

RIFF-header image-size %b1 subtract-green-tx
%b1 predictor-tx %b0 color-cache-info
%b0 prefix-codes lz77-coded-image