연습용으로 다양한 형식의 SDL_Surfaces를 내보내는 C 라이브러리를 작성 중이며 지금까지 BMP, TGA 및 PCX 형식을 사용했습니다. 이제는 GIF 형식으로 작업하고 있으며 작동하는 데 아주 가깝다고 생각합니다. 내 구현은 수정 된 버전 this one입니다.GIF LZW 압축 된 스트림의 인코딩 오류
현재 문제는 GIF LZW 압축 이미지 데이터 하위 블록을 작성하는 것입니다. 첫 번째 하위 블록에서 위치 208까지 모든 것이 부드럽게 진행됩니다. 원본 파일의 3 바이트는 (위치 207부터 시작) : "B8 29 B2"(16 진수), "B8 41 B2"입니다. 그 후에 바이트는 다시 까지 "동기화"됩니다. 압축 된 스트림을 더 내려 보면 비슷한 차이점을 발견 할 수 있습니다. 아마도 이 첫 번째 오류로 인해 발생했습니다. 내 파일도 원래 파일보다 짧습니다.
lxw_entry 구조체의 유형을 uint16_t에서 int로 변경하여 -1을 "빈"항목으로 허용하려면 0이 유효한 항목이므로 허용해야합니다. 그것은 실제로 압축 된 스트림에서 차이 을 만들지 않았습니다. 원래 구현에서는 초기화되지 않은 데이터 을 사용하여 대신 빈 항목을 표시합니다.
내가 예상치 못한 208 위치에 다른 코드를 얻는 이유는 내가 사전 값을 잘못 읽고 있다고 생각합니다. 그렇지 않으면 내 비트 포장이 잘못되었습니다.
필자는 압축 코드를 삭제 한 버전을 추가했습니다. 무엇이 문제일까요? 또한, 어떻게하면 내 "사전"데이터 구조를보다 잘 만들거나 비트 스트림을 더 빠르게 작성할 수 있습니까?
마지막으로, 나는 또한 내가 여기에 몇 가지 코드를 최적화 할 수 있음을 알고 있어요 및
static Uint8 bit_count = 0;
static Uint8 block_pos = 0;
int LZW_PackBits(SDL_RWops *dst, Uint8 *block, int code, Uint8 bits) {
Uint8 out = 0;
while (out != bits) {
if (bit_count == 8) {
bit_count = 0;
if (block_pos == 254) { // Thus 254 * 8 + 8 == 2040 -> 2040/8 = 255 -> buffer full
++block_pos;
SDL_RWwrite(dst, &block_pos, 1, 1);
SDL_RWwrite(dst, &block[0], 1, block_pos);
memset(block, 0, block_pos);
block_pos = 0;
} else
++block_pos;
}
block[block_pos] |= (code >> out & 0x1) << bit_count;
++bit_count; ++out;
}
return 1;
}
#define LZW_MAX_BITS 12
#define LZW_START_BITS 9
#define LZW_CLEAR_CODE 256
#define LZW_END_CODE 257
#define LZW_ALPHABET_SIZE 256
typedef struct {
int next[LZW_ALPHABET_SIZE]; // int so that -1 is allowed
} lzw_entry;
int table_size = 1 << LZW_MAX_BITS; // 2^12 = 4096
lzw_entry *lzw_table = (lzw_entry*)malloc(sizeof(lzw_entry) * table_size);
for (i = 0; i < table_size; ++i)
memset(&lzw_table[i].next[0], -1, sizeof(int) * LZW_ALPHABET_SIZE);
Uint8 block[255];
memset(&block[0], 0, 255);
Uint16 next_entry = LZW_END_CODE + 1;
Uint8 out_len = LZW_START_BITS;
Uint8 next_byte = 0;
int input = 0;
int nc = 0;
LZW_PackBits(dst, block, clear_code, out_len);
Uint8 *pos = ... // Start of image data
Uint8 *end = ... // End of image data
input = *pos++;
while (pos < end) {
next_byte = *pos++;
nc = lzw_table[input].next[next_byte];
if (nc >= 0) {
input = nc;
continue;
} else {
LZW_PackBits(dst, block, input, out_len);
nc = lzw_table[input].next[next_byte] = next_entry++;
input = next_byte;
}
if (next_entry == (1 << out_len)) { // Next code requires more bits
++out_len;
if (out_len > LZW_MAX_BITS) {
// Reset table
LZW_PackBits(dst, block, clear_code, out_len - 1);
out_len = LZW_START_BITS;
next_entry = LZW_END_CODE + 1;
for (i = 0; i < table_size; ++i)
memset(&lzw_table[i].next[0], -1, sizeof(int) * LZW_ALPHABET_SIZE);
}
}
}
// Write remaining stuff including current code (not shown)
LZW_PackBits(dst, block, end_code, out_len);
++block_pos;
SDL_RWwrite(dst, &block[0], 1, block_pos);
SDL_RWwrite(dst, &zero_byte, 1, 1);
const Uint8 trailer = 0x3b; // ';'
SDL_RWwrite(dst, &trailer, 1, 1);
UPDATE가 :) : 좀 더 많은 테스트를 수행하고, 비트 패킹 알고리즘을 구현했습니다 그 아키 Suihkonen 제안했다. 그것은 필자가 어떻게 든 lzw_table 구조체에서 코드를 잘못 찾고/저장하고 오류가 메인 루프에 있음을 알리는 눈에 띄는 차이가 없다.
예. LZW의 GIF 버전에서는 압축 된 데이터를 하위 블록으로 압축해야합니다. 각 하위 블록은 하위 블록에 얼마나 많은 바이트가 있는지 알려주는 바이트 (1-255)이며 압축 된 데이터의 많은 바이트가 이어집니다. 따라서 대부분의 블록에는 마지막 블록을 제외하고 255 블록 수가 포함됩니다. 비트 팩을 가져 주셔서 감사합니다. 나는 유사한 것을 쓰려고 노력했지만, 어쨌든, 나는 그저 내 머리를 감쌀 수 없었다. 나는 'code + 1'에 대한 당신의 생각을 좋아합니다.해시 테이블을 만들 수는 있지만이 아이디어는 처음부터 간단하다고 생각했습니다. – NordCoder
오른쪽. 그걸 기억하지 못했습니다. 2012 년에 폐기 된 것 같습니다 ... 몇 개의 코드를 정리해야합니까? 1M에서 ~ 3840 점령 된 슬롯의 주소를 배열에 저장하지 않는 이유는 무엇입니까? (어쨌든, 우선 코드 작동을 얻는 것입니다 ...) –
. 스펙은 최대 비트 길이 12를 허용하므로, 4096 코드를 지워야합니다 (이 구현으로는 그 이상입니다). 나는 당신이 조금 더 설명 할 필요가 있다고 생각하지만, 그렇다 : 동의한다 : 코드가 제일 먼저 작동해야한다 : – NordCoder