Lz4: Issue decompressing image loaded with stb_image.h

Created on 8 Nov 2019  路  4Comments  路  Source: lz4/lz4

I am able to compress the image just fine but when I am decompressing the image, the result of the decompress is always negative.

I have gone through a few trials (mostly dealing with stb_image result being unsigned char and lz4 being char) but I am unable to figure this out.

Full repro (VS 2017):

https://github.com/KyleLangley/lz4_compression_problem

cpp:

#define _CRT_SECURE_NO_WARNINGS

#define STB_IMAGE_IMPLEMENTATION
#include "stb/stb_image.h"

#include "lz4/lz4hc.h"

#define Assert(Expression) if(!(Expression)) {*(int *)0 = 0;}

#define COMPRESSED_FILE_NAME "compressed_parrot.lz"
#define SAVE_TO_CONTENT_PATH(FileName) "content/"##FileName
#define LOAD_FROM_CONTENT_PATH(FileName) SAVE_TO_CONTENT_PATH(FileName)

struct ofile
{
    char* Ptr;
    int Size;
};

struct texture
{
    int Width;
    int Height;
    int Channels;
    unsigned char* Ptr;
};

struct compressed_file
{
    char* Ptr;
    int Size;
    int OriginalSize;
};

static void LoadTextureFromFile(const char* FileName, texture& OutResult)
{
    // returns "unsigned char*"
    OutResult.Ptr = stbi_load(FileName, &OutResult.Width, &OutResult.Height, &OutResult.Channels, 0);
}

static void CompressFile(char* Ptr, const int PtrSize, compressed_file& OutCompressedFile)
{
    LZ4_streamHC_t lz4Stream_body = { 0 };
    LZ4_streamHC_t* lz4Stream = &lz4Stream_body;

    if (Ptr != nullptr && PtrSize > 0)
    {
        OutCompressedFile.OriginalSize = LZ4_compressBound(PtrSize);
        OutCompressedFile.Ptr = (char*)malloc(OutCompressedFile.OriginalSize);

        OutCompressedFile.Size = LZ4_compress_default(Ptr, OutCompressedFile.Ptr, PtrSize, OutCompressedFile.OriginalSize);
    }
}

static void DecompressFile(const compressed_file& Compresssed, const ofile& FromDisk, ofile& OutResult)
{
    LZ4_streamDecode_t lz4StreamDecode_body = { 0 };
    LZ4_streamDecode_t* lz4StreamDecode = &lz4StreamDecode_body;

    OutResult.Ptr = (char*)malloc(Compresssed.OriginalSize);
    OutResult.Size = LZ4_decompress_safe(FromDisk.Ptr, OutResult.Ptr, FromDisk.Size, Compresssed.OriginalSize);
}

static void LoadFileFromDisk(const char* FileName, ofile& OutResult)
{
    FILE* File = fopen(FileName, "r");
    if (File != nullptr)
    {
        fseek(File, 0, SEEK_END);
        OutResult.Size = ftell(File);
        fseek(File, 0, SEEK_SET);

        OutResult.Ptr = (char*)malloc(OutResult.Size);

        size_t IterationBufferSizeRead = 0;
        while (!feof(File))
        {
            IterationBufferSizeRead = fread(OutResult.Ptr, 1, OutResult.Size - 1, File);
            OutResult.Ptr[IterationBufferSizeRead] = 0;
        }
        fclose(File);
    }
}

static void SaveBinaryFileToDisk(const char* FileName, char* Ptr, const int PtrSize)
{
    FILE* File = fopen(FileName, "wb");
    if (nullptr == File)
    {
        return;
    }
    else
    {
        const size_t WriteSize = fwrite(Ptr, PtrSize, 1, File);
        if (ferror(File))
        {
            printf("Error writing to file %s,", FileName);
        }

    }
    fclose(File);
}

static void SaveTest(compressed_file& Compressed)
{
    texture T = {};
    LoadTextureFromFile(LOAD_FROM_CONTENT_PATH("parrot.tga"), T);

    if (T.Ptr != nullptr)
    {
        CompressFile((char*)T.Ptr, T.Width * T.Height * T.Channels, Compressed);
        if (Compressed.Ptr != nullptr && Compressed.Ptr > 0)
        {
            SaveBinaryFileToDisk(SAVE_TO_CONTENT_PATH(COMPRESSED_FILE_NAME), Compressed.Ptr, Compressed.Size);
        }

        free(Compressed.Ptr);
    }

    stbi_image_free(T.Ptr);
}

static void LoadTest(const compressed_file& CompressedFile)
{
    ofile LoadedCompressed = {};
    LoadFileFromDisk(LOAD_FROM_CONTENT_PATH(COMPRESSED_FILE_NAME), LoadedCompressed);

    if (LoadedCompressed.Ptr != nullptr)
    {
        ofile DecompressedFile = {};

        DecompressFile(CompressedFile, LoadedCompressed, DecompressedFile);

        bool bValidDecompress = LoadedCompressed.Ptr != nullptr && DecompressedFile.Size == CompressedFile.OriginalSize;

        free(DecompressedFile.Ptr);
        free(LoadedCompressed.Ptr);
        free(CompressedFile.Ptr);

        // Failed.... DecompressedFile.Size is negative... 
        Assert(bValidDecompress);
    }
}

int main(int argc, char** argv)
{
    compressed_file CompressedFile = {};
    SaveTest(CompressedFile);

    LoadTest(CompressedFile);
    return 0;
}
question

All 4 comments

Could you try to open files with "rb" parameter ?

Tried with "rb" and I did get a different result but still negative.

"r" == -8915916
"rb" == -4

Thanks

Continuing to test..

I have copied over the HCStreaming_ringBuffer.c example and ran through the case of using the direct FILE* type, which does compress and decompress without error. However the "compare" function does not succeed. Though when opening the resulting FILE output of the decompressed file in photoshop, it is identical to the original.

With that said, considering the FILE implementation does work as expected, I believe my original issue is something I am doing incorrectly. Still looking for any guidance as having the extra floating FILE output saved to disk when loaded the decompressed is something I would like to avoid.

Thanks.

To followup, as a lesson in "it should of been obvious":

IterationBufferSizeRead = fread(OutResult.Ptr, 1, OutResult.Size - 1, File);

OutResult.Size - 1 causes an of by 1 error in the read, causing the decompress to fail because it cant resolve the end of the file.

Removing the " - 1" fixes this problem.

Was this page helpful?
0 / 5 - 0 ratings