C 解码字典中尚未出现的TIFF LZW代码

C 解码字典中尚未出现的TIFF LZW代码,c,compression,tiff,lzw,C,Compression,Tiff,Lzw,我制作了一个LZW压缩TIFF图像的解码器,所有的部分都工作了,它可以在不同的比特深度下解码大图像,有或没有水平预测,只有一种情况除外。虽然它可以解码大多数程序编写的文件(如Photoshop和Krita,具有各种编码选项),但ImageMagick的转换创建的文件有一点非常奇怪,它生成的LZW代码还没有出现在字典中,我不知道如何处理它 在大多数情况下,LZW流中的9到12位代码还没有在字典中,这是我的解码算法将尝试放入字典的下一个代码(虽然我的算法在包含这种情况的图像上失败,但我不确定这是否应

我制作了一个LZW压缩TIFF图像的解码器,所有的部分都工作了,它可以在不同的比特深度下解码大图像,有或没有水平预测,只有一种情况除外。虽然它可以解码大多数程序编写的文件(如Photoshop和Krita,具有各种编码选项),但ImageMagick的
转换
创建的文件有一点非常奇怪,它生成的LZW代码还没有出现在字典中,我不知道如何处理它

在大多数情况下,LZW流中的9到12位代码还没有在字典中,这是我的解码算法将尝试放入字典的下一个代码(虽然我的算法在包含这种情况的图像上失败,但我不确定这是否应该是一个问题),但有时它甚至可能是未来的数百个代码。在一种情况下,清除代码(256)后的第一个代码是364,这似乎是不可能的,因为清除代码清除了我的字典中的所有代码258及以上,在另一种情况下,当我的字典只升到317时,代码是501

我不知道如何处理它,但似乎只有我一个人有这个问题,其他程序中的解码器加载这样的图像很好。那么他们是如何做到的呢

这是我的解码算法的核心,显然由于涉及的代码太多,我无法以紧凑的方式提供完整的可编译代码,但由于这是一个算法逻辑的问题,这应该足够了。它严格遵循官方(第61页)中描述的算法,事实上大部分规范的伪代码都在注释中

void tiff_lzw_decode(uint8_t *coded, buffer_t *dec)
{
    buffer_t word={0}, outstring={0};
    size_t coded_pos;   // position in bits
    int i, new_index, code, maxcode, bpc;

    buffer_t *dict={0};
    size_t dict_as=0;

    bpc = 9;            // starts with 9 bits per code, increases later
    tiff_lzw_calc_maxcode(bpc, &maxcode);
    new_index = 258;        // index at which new dict entries begin
    coded_pos = 0;          // bit position

    lzw_dict_init(&dict, &dict_as);

    while ((code = get_bits_in_stream(coded, coded_pos, bpc)) != 257)   // while ((Code = GetNextCode()) != EoiCode) 
    {
        coded_pos += bpc;

        if (code >= new_index)
            printf("Out of range code %d (new_index %d)\n", code, new_index);

        if (code == 256)                        // if (Code == ClearCode)
        {
            lzw_dict_init(&dict, &dict_as);             // InitializeTable();
            bpc = 9;
            tiff_lzw_calc_maxcode(bpc, &maxcode);
            new_index = 258;

            code = get_bits_in_stream(coded, coded_pos, bpc);   // Code = GetNextCode();
            coded_pos += bpc;

            if (code == 257)                    // if (Code == EoiCode)
                break;

            append_buf(dec, &dict[code]);               // WriteString(StringFromCode(Code));

            clear_buf(&word);
            append_buf(&word, &dict[code]);             // OldCode = Code;
        }
        else if (code < 4096)
        {
            if (dict[code].len)                 // if (IsInTable(Code))
            {
                append_buf(dec, &dict[code]);           // WriteString(StringFromCode(Code));

                lzw_add_to_dict(&dict, &dict_as, new_index, 0, word.buf, word.len, &bpc);
                lzw_add_to_dict(&dict, &dict_as, new_index, 1, dict[code].buf, 1, &bpc);    // AddStringToTable
                new_index++;
                tiff_lzw_calc_bpc(new_index, &bpc, &maxcode);

                clear_buf(&word);
                append_buf(&word, &dict[code]);         // OldCode = Code;
            }
            else
            {
                clear_buf(&outstring);
                append_buf(&outstring, &word);
                bufwrite(&outstring, word.buf, 1);      // OutString = StringFromCode(OldCode) + FirstChar(StringFromCode(OldCode));

                append_buf(dec, &outstring);            // WriteString(OutString);

                lzw_add_to_dict(&dict, &dict_as, new_index, 0, outstring.buf, outstring.len, &bpc); // AddStringToTable
                new_index++;
                tiff_lzw_calc_bpc(new_index, &bpc, &maxcode);

                clear_buf(&word);
                append_buf(&word, &dict[code]);         // OldCode = Code;
            }
        }

    }

    free_buf(&word);
    free_buf(&outstring);
    for (i=0; i < dict_as; i++)
        free_buf(&dict[i]);
    free(dict);
}

如果有人坚持要潜入我的代码(这应该是不必要的,而且它是一个很大的库)。

伪造的代码来自于试图解码超出我们预期的代码。问题是,LZW条有时可能不会以信息257代码的结尾结束,因此当输出一定数量的解码字节时,解码循环必须停止。每个条带的字节数由TIFF标记ROWSPERSTRIP*IMAGEWITH*BITSPERSAMPLE/8确定,如果PLANARCONFIG为1(表示交错通道,而非平面通道),则将其全部乘以SAMPLESPERPIXEL。因此,除了在遇到代码257时停止解码循环外,还必须在达到解码字节数后停止循环。

伪代码来自于试图解码超出我们预期的部分。问题是,LZW条有时可能不会以信息257代码的结尾结束,因此当输出一定数量的解码字节时,解码循环必须停止。每个条带的字节数由TIFF标记ROWSPERSTRIP*IMAGEWITH*BITSPERSAMPLE/8确定,如果PLANARCONFIG为1(表示交错通道,而非平面通道),则将其全部乘以SAMPLESPERPIXEL。因此,除了在遇到代码257时停止解码循环外,还必须在达到解码字节数后停止循环。

这可能是一个愚蠢的问题,但我猜您已经阅读了libtiff是如何做到这一点的?这似乎是对的:@cgohlke我试图在问题中明确表示,跟踪代码大小不可能是一个问题。很明显,这是一个理论问题,ImageMagick显然认为有可能有如此高的代码,而且大多数解码器似乎都知道如何很好地处理它。@jcupitt我知道,尽管我尽量避免试图根据不追求清晰的代码得出结论。但是,至少在遵循一个清晰代码LIbTIFF的情况下,会考虑下面的代码255以上是腐败的,这也是我的代码所做的,但这无济于事。@ MichelRouzic,我尝试了您的示例文件与我的库(使用LIPTIFF为TIFF加载),它对我来说很好。ImageMagick也使用libtiff进行TIFF写入,所以我认为其中一定有秘密。我会在调试器中尝试
转换奇怪的.tif x.png
,然后观察
LZWDecode
(或任何最终被调用的内容)的执行情况。事后看来,这应该是显而易见的,但我想了想,想不出如何避免阅读过多(我想的是如何限制输入,我没有想到计算输出大小),所以我没有这么做。但是,该规范的伪代码非常明确,您可能会认为它包含了这样一个考虑因素,但是很好。至少我现在明白了。这可能是一个愚蠢的问题,但我想你已经读过libtiff是如何做到这一点的?这似乎是对的:@cgohlke我试图在问题中明确表示,跟踪代码大小不可能是一个问题。很明显,这是一个理论问题,ImageMagick显然认为有可能有如此高的代码,而且大多数解码器似乎都知道如何很好地处理它。@jcupitt我知道,尽管我尽量避免试图根据不追求清晰的代码得出结论。但是,至少在遵循一个清晰代码LIbTIFF的情况下,会考虑下面的代码255以上是腐败的,这也是我的代码所做的,但这无济于事。@ MichelRouzic,我尝试了您的示例文件与我的库(使用LIPTIFF为TIFF加载),它对我来说很好。ImageMagick也使用libtiff进行TIFF写入,所以我认为其中一定有秘密。我会在调试器中尝试
转换奇怪的.tif x.png
,然后观察
LZWDecode
(或任何最终被调用的内容)的执行情况。事后看来,这应该是显而易见的,但我想了想,想不出如何避免阅读过多(我想的是如何限制输入,我没有想到计算输出大小),所以我没有这么做。但是,该规范的伪代码非常明确,您可能会认为它包含了这样一个考虑因素,但是很好。至少我现在明白了。
#include "loadtiff.h"
#include "loadtiff.c"
void loadtiff_test(char *path)
{
    int width, height, format;
    floadtiff(fopen(path, "rb"), &width, &height, &format);
}