HRESULT UncompressPixels()

in DirectXTex/DirectXTexTGA.cpp [284:805]


    HRESULT UncompressPixels(
        _In_reads_bytes_(size) const void* pSource,
        size_t size,
        TGA_FLAGS flags,
        _In_ const Image* image,
        _In_ uint32_t convFlags) noexcept
    {
        assert(pSource && size > 0);

        if (!image || !image->pixels)
            return E_POINTER;

        // Compute TGA image data pitch
        size_t rowPitch, slicePitch;
        HRESULT hr = ComputePitch(image->format, image->width, image->height, rowPitch, slicePitch,
            (convFlags & CONV_FLAGS_EXPAND) ? CP_FLAGS_24BPP : CP_FLAGS_NONE);
        if (FAILED(hr))
            return hr;

        auto sPtr = static_cast<const uint8_t*>(pSource);
        const uint8_t* endPtr = sPtr + size;

        bool opaquealpha = false;

        switch (image->format)
        {
        //--------------------------------------------------------------------------- 8-bit
        case DXGI_FORMAT_R8_UNORM:
            for (size_t y = 0; y < image->height; ++y)
            {
                size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);
                assert(offset < rowPitch);

                uint8_t* dPtr = image->pixels
                    + (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1)))
                    + offset;

                for (size_t x = 0; x < image->width; )
                {
                    if (sPtr >= endPtr)
                        return E_FAIL;

                    if (*sPtr & 0x80)
                    {
                        // Repeat
                        size_t j = size_t(*sPtr & 0x7F) + 1;
                        if (++sPtr >= endPtr)
                            return E_FAIL;

                        for (; j > 0; --j, ++x)
                        {
                            if (x >= image->width)
                                return E_FAIL;

                            *dPtr = *sPtr;

                            if (convFlags & CONV_FLAGS_INVERTX)
                                --dPtr;
                            else
                                ++dPtr;
                        }

                        ++sPtr;
                    }
                    else
                    {
                        // Literal
                        size_t j = size_t(*sPtr & 0x7F) + 1;
                        ++sPtr;

                        if (sPtr + j > endPtr)
                            return E_FAIL;

                        for (; j > 0; --j, ++x)
                        {
                            if (x >= image->width)
                                return E_FAIL;

                            *dPtr = *(sPtr++);

                            if (convFlags & CONV_FLAGS_INVERTX)
                                --dPtr;
                            else
                                ++dPtr;
                        }
                    }
                }
            }
            break;

        //-------------------------------------------------------------------------- 16-bit
        case DXGI_FORMAT_B5G5R5A1_UNORM:
        {
            uint32_t minalpha = 255;
            uint32_t maxalpha = 0;

            for (size_t y = 0; y < image->height; ++y)
            {
                size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);
                assert(offset * 2 < rowPitch);

                auto dPtr = reinterpret_cast<uint16_t*>(image->pixels
                    + (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1))))
                    + offset;

                for (size_t x = 0; x < image->width; )
                {
                    if (sPtr >= endPtr)
                        return E_FAIL;

                    if (*sPtr & 0x80)
                    {
                        // Repeat
                        size_t j = size_t(*sPtr & 0x7F) + 1;
                        ++sPtr;

                        if (sPtr + 1 >= endPtr)
                            return E_FAIL;

                        auto t = static_cast<uint16_t>(uint32_t(*sPtr) | uint32_t(*(sPtr + 1u) << 8));

                        uint32_t alpha = (t & 0x8000) ? 255 : 0;
                        minalpha = std::min(minalpha, alpha);
                        maxalpha = std::max(maxalpha, alpha);

                        sPtr += 2;

                        for (; j > 0; --j, ++x)
                        {
                            if (x >= image->width)
                                return E_FAIL;

                            *dPtr = t;

                            if (convFlags & CONV_FLAGS_INVERTX)
                                --dPtr;
                            else
                                ++dPtr;
                        }
                    }
                    else
                    {
                        // Literal
                        size_t j = size_t(*sPtr & 0x7F) + 1;
                        ++sPtr;

                        if (sPtr + (j * 2) > endPtr)
                            return E_FAIL;

                        for (; j > 0; --j, ++x)
                        {
                            if (x >= image->width)
                                return E_FAIL;

                            auto t = static_cast<uint16_t>(uint32_t(*sPtr) | uint32_t(*(sPtr + 1u) << 8));

                            uint32_t alpha = (t & 0x8000) ? 255 : 0;
                            minalpha = std::min(minalpha, alpha);
                            maxalpha = std::max(maxalpha, alpha);

                            sPtr += 2;
                            *dPtr = t;

                            if (convFlags & CONV_FLAGS_INVERTX)
                                --dPtr;
                            else
                                ++dPtr;
                        }
                    }
                }
            }

            // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
            if (maxalpha == 0 && !(flags & TGA_FLAGS_ALLOW_ALL_ZERO_ALPHA))
            {
                opaquealpha = true;
                hr = SetAlphaChannelToOpaque(image);
                if (FAILED(hr))
                    return hr;
            }
            else if (minalpha == 255)
            {
                opaquealpha = true;
            }
        }
        break;

        //------------------------------------------------------ 24/32-bit (with swizzling)
        case DXGI_FORMAT_R8G8B8A8_UNORM:
        {
            uint32_t minalpha = 255;
            uint32_t maxalpha = 0;

            for (size_t y = 0; y < image->height; ++y)
            {
                size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);

                auto dPtr = reinterpret_cast<uint32_t*>(image->pixels
                    + (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1))))
                    + offset;

                for (size_t x = 0; x < image->width; )
                {
                    if (sPtr >= endPtr)
                        return E_FAIL;

                    if (*sPtr & 0x80)
                    {
                        // Repeat
                        size_t j = size_t(*sPtr & 0x7F) + 1;
                        ++sPtr;

                        uint32_t t;
                        if (convFlags & CONV_FLAGS_EXPAND)
                        {
                            assert(offset * 3 < rowPitch);

                            if (sPtr + 2 >= endPtr)
                                return E_FAIL;

                            // BGR -> RGBA
                            t = uint32_t(*sPtr << 16) | uint32_t(*(sPtr + 1) << 8) | uint32_t(*(sPtr + 2)) | 0xFF000000;
                            sPtr += 3;

                            minalpha = maxalpha = 255;
                        }
                        else
                        {
                            assert(offset * 4 < rowPitch);

                            if (sPtr + 3 >= endPtr)
                                return E_FAIL;

                            // BGRA -> RGBA
                            uint32_t alpha = *(sPtr + 3);
                            t = uint32_t(*sPtr << 16) | uint32_t(*(sPtr + 1) << 8) | uint32_t(*(sPtr + 2)) | uint32_t(alpha << 24);

                            minalpha = std::min(minalpha, alpha);
                            maxalpha = std::max(maxalpha, alpha);

                            sPtr += 4;
                        }

                        for (; j > 0; --j, ++x)
                        {
                            if (x >= image->width)
                                return E_FAIL;

                            *dPtr = t;

                            if (convFlags & CONV_FLAGS_INVERTX)
                                --dPtr;
                            else
                                ++dPtr;
                        }
                    }
                    else
                    {
                        // Literal
                        size_t j = size_t(*sPtr & 0x7F) + 1;
                        ++sPtr;

                        if (convFlags & CONV_FLAGS_EXPAND)
                        {
                            if (sPtr + (j * 3) > endPtr)
                                return E_FAIL;
                        }
                        else
                        {
                            if (sPtr + (j * 4) > endPtr)
                                return E_FAIL;
                        }

                        for (; j > 0; --j, ++x)
                        {
                            if (x >= image->width)
                                return E_FAIL;

                            if (convFlags & CONV_FLAGS_EXPAND)
                            {
                                assert(offset * 3 < rowPitch);

                                if (sPtr + 2 >= endPtr)
                                    return E_FAIL;

                                // BGR -> RGBA
                                *dPtr = uint32_t(*sPtr << 16) | uint32_t(*(sPtr + 1) << 8) | uint32_t(*(sPtr + 2)) | 0xFF000000;
                                sPtr += 3;

                                minalpha = maxalpha = 255;
                            }
                            else
                            {
                                assert(offset * 4 < rowPitch);

                                if (sPtr + 3 >= endPtr)
                                    return E_FAIL;

                                // BGRA -> RGBA
                                uint32_t alpha = *(sPtr + 3);
                                *dPtr = uint32_t(*sPtr << 16) | uint32_t(*(sPtr + 1) << 8) | uint32_t(*(sPtr + 2)) | uint32_t(alpha << 24);

                                minalpha = std::min(minalpha, alpha);
                                maxalpha = std::max(maxalpha, alpha);

                                sPtr += 4;
                            }

                            if (convFlags & CONV_FLAGS_INVERTX)
                                --dPtr;
                            else
                                ++dPtr;
                        }
                    }
                }
            }

            // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
            if (maxalpha == 0 && !(flags & TGA_FLAGS_ALLOW_ALL_ZERO_ALPHA))
            {
                opaquealpha = true;
                hr = SetAlphaChannelToOpaque(image);
                if (FAILED(hr))
                    return hr;
            }
            else if (minalpha == 255)
            {
                opaquealpha = true;
            }
        }
        break;

        //-------------------------------------------------------------------- 32-bit (BGR)
        case DXGI_FORMAT_B8G8R8A8_UNORM:
        {
            assert((convFlags & CONV_FLAGS_EXPAND) == 0);

            uint32_t minalpha = 255;
            uint32_t maxalpha = 0;

            for (size_t y = 0; y < image->height; ++y)
            {
                size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);

                auto dPtr = reinterpret_cast<uint32_t*>(image->pixels
                    + (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1))))
                    + offset;

                for (size_t x = 0; x < image->width; )
                {
                    if (sPtr >= endPtr)
                        return E_FAIL;

                    if (*sPtr & 0x80)
                    {
                        // Repeat
                        size_t j = size_t(*sPtr & 0x7F) + 1;
                        ++sPtr;

                        assert(offset * 4 < rowPitch);

                        if (sPtr + 3 >= endPtr)
                            return E_FAIL;

                        uint32_t alpha = *(sPtr + 3);

                        auto t = *reinterpret_cast<const uint32_t*>(sPtr);

                        minalpha = std::min(minalpha, alpha);
                        maxalpha = std::max(maxalpha, alpha);

                        sPtr += 4;

                        for (; j > 0; --j, ++x)
                        {
                            if (x >= image->width)
                                return E_FAIL;

                            *dPtr = t;

                            if (convFlags & CONV_FLAGS_INVERTX)
                                --dPtr;
                            else
                                ++dPtr;
                        }
                    }
                    else
                    {
                        // Literal
                        size_t j = size_t(*sPtr & 0x7F) + 1;
                        ++sPtr;

                        if (sPtr + (j * 4) > endPtr)
                            return E_FAIL;

                        for (; j > 0; --j, ++x)
                        {
                            if (x >= image->width)
                                return E_FAIL;

                            assert(offset * 4 < rowPitch);

                            if (sPtr + 3 >= endPtr)
                                return E_FAIL;

                            uint32_t alpha = *(sPtr + 3);
                            *dPtr = *reinterpret_cast<const uint32_t*>(sPtr);

                            minalpha = std::min(minalpha, alpha);
                            maxalpha = std::max(maxalpha, alpha);

                            sPtr += 4;

                            if (convFlags & CONV_FLAGS_INVERTX)
                                --dPtr;
                            else
                                ++dPtr;
                        }
                    }
                }
            }

            // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
            if (maxalpha == 0 && !(flags & TGA_FLAGS_ALLOW_ALL_ZERO_ALPHA))
            {
                opaquealpha = true;
                hr = SetAlphaChannelToOpaque(image);
                if (FAILED(hr))
                    return hr;
            }
            else if (minalpha == 255)
            {
                opaquealpha = true;
            }
        }
        break;

        //-------------------------------------------------------------------- 24-bit (BGR)
        case DXGI_FORMAT_B8G8R8X8_UNORM:
        {
            assert((convFlags & CONV_FLAGS_EXPAND) != 0);

            for (size_t y = 0; y < image->height; ++y)
            {
                size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);

                auto dPtr = reinterpret_cast<uint32_t*>(image->pixels
                    + (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1))))
                    + offset;

                for (size_t x = 0; x < image->width; )
                {
                    if (sPtr >= endPtr)
                        return E_FAIL;

                    if (*sPtr & 0x80)
                    {
                        // Repeat
                        size_t j = size_t(*sPtr & 0x7F) + 1;
                        ++sPtr;

                        assert(offset * 3 < rowPitch);

                        if (sPtr + 2 >= endPtr)
                            return E_FAIL;

                        uint32_t t = uint32_t(*sPtr) | uint32_t(*(sPtr + 1) << 8) | uint32_t(*(sPtr + 2) << 16);
                        sPtr += 3;

                        for (; j > 0; --j, ++x)
                        {
                            if (x >= image->width)
                                return E_FAIL;

                            *dPtr = t;

                            if (convFlags & CONV_FLAGS_INVERTX)
                                --dPtr;
                            else
                                ++dPtr;
                        }
                    }
                    else
                    {
                        // Literal
                        size_t j = size_t(*sPtr & 0x7F) + 1;
                        ++sPtr;

                        if (sPtr + (j * 3) > endPtr)
                            return E_FAIL;

                        for (; j > 0; --j, ++x)
                        {
                            if (x >= image->width)
                                return E_FAIL;

                            assert(offset * 3 < rowPitch);

                            if (sPtr + 2 >= endPtr)
                                return E_FAIL;

                            *dPtr = uint32_t(*sPtr) | uint32_t(*(sPtr + 1) << 8) | uint32_t(*(sPtr + 2) << 16);
                            sPtr += 3;

                            if (convFlags & CONV_FLAGS_INVERTX)
                                --dPtr;
                            else
                                ++dPtr;
                        }
                    }
                }
            }
        }
        break;

        //---------------------------------------------------------------------------------
        default:
            return E_FAIL;
        }

        return opaquealpha ? S_FALSE : S_OK;
    }