Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Wrong channel order when reading file saved by TINYEXR #348

@Thomaswang0822

Description

@Thomaswang0822

Steps to reproduce: run the function below, open exr with TEV, inspect the channel values.

In the multi-line comment where we set channel names, I suppose TEV behave like that.
Is my guess correct? If yes, could it be fixed (or is it fixed already? I am using version 1.26), since both TEV and TinyEXR are very popular tools.

    bool TestTinyExrWrite()
    {
        EXRHeader header;
        EXRImage image;
        InitEXRHeader(&header);
        InitEXRImage(&image);

        int width = 1920;
        int height = 1080;
        int num_channels = 4;

        // Set up header
        header.num_channels = num_channels;
        header.channels = new EXRChannelInfo[num_channels];
        header.pixel_types = new int[num_channels];
        header.requested_pixel_types = new int[num_channels];

        const char names[4] = { 'R', 'G', 'B', 'A' };
        /// TEV Viewer works like this:
        /// It "blindly" reorders the channels in alphabetical order,
        /// which means even if you pack data in RGBA order and set header.channels also,
        /// it displays data wrongly as ABGR.
        /// In the channel names below, where NONE is a valid common channel name and H comes first,
        /// no matter how we order them, 
        /// TEV will show a single H-channel image with 0.9f as value, because it comes first.
        //const char names[4] = { 'O', 'H', 'P', 'Q' };
        for (int i = 0; i < num_channels; i++) {
            header.channels[i].name[0] = names[i];
            header.channels[i].name[1] = '\0';
            header.pixel_types[i] = TINYEXR_PIXELTYPE_HALF;
            header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_HALF;
        }
        header.compression_type = TINYEXR_COMPRESSIONTYPE_NONE; // No compression for simplicity

        // Allocate data for each channel
        image.num_channels = num_channels;
        image.width = width;
        image.height = height;
        image.images = new unsigned char* [num_channels];

        // Create distinct test values for each channel:
        // Channel A: all 1.0f
        // Channel R: all 0.9f
        // Channel G: all 0.6f
        // Channel B: all 0.1f
        auto fToU16 = [](float value) {
            tinyexr::FP32 f32; 
            f32.f = value;
            return  tinyexr::float_to_half_full(f32).u;
            };
        std::vector<uint16_t> dataR(width * height, fToU16(0.9f));
        std::vector<uint16_t> dataG(width * height, fToU16(0.6f));
        std::vector<uint16_t> dataB(width * height, fToU16(0.1f));
        std::vector<uint16_t> dataA(width * height, fToU16(1.0f));

        image.images[0] = reinterpret_cast<unsigned char*>(dataR.data());
        image.images[1] = reinterpret_cast<unsigned char*>(dataG.data());
        image.images[2] = reinterpret_cast<unsigned char*>(dataB.data());
        image.images[3] = reinterpret_cast<unsigned char*>(dataA.data());

        // Save the EXR file
        const char* err = nullptr;
        int ret = SaveEXRImageToFile(&image, &header, <ANY LOCATION>, &err);
        if (ret != TINYEXR_SUCCESS) {
            printf("Error: %s\n", err);
            return 1;
        }

        // Cleanup
        delete[] header.channels;
        delete[] header.requested_pixel_types;
        delete[] image.images;

        printf("EXR file 'test.exr' created successfully.\n");
        return true;
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions