HRESULT CreateTextureFromDDS()

in DDSTextureLoader/DDSTextureLoader9.cpp [773:1085]


    HRESULT CreateTextureFromDDS(
        _In_ LPDIRECT3DDEVICE9 device,
        _In_ const DDS_HEADER* header,
        _In_reads_bytes_(bitSize) const uint8_t* bitData,
        _In_ size_t bitSize,
        _In_ DWORD usage,
        _In_ D3DPOOL pool,
        _Outptr_ LPDIRECT3DBASETEXTURE9* texture,
        bool generateMipsIfMissing) noexcept
    {
        HRESULT hr = S_OK;

        UINT iWidth = header->width;
        UINT iHeight = header->height;

        UINT iMipCount = header->mipMapCount;
        if (0 == iMipCount)
        {
            iMipCount = 1;
        }

        // Bound sizes (for security purposes we don't trust DDS file metadata larger than the D3D 10 hardware requirements)
        if (iMipCount > 14u /*D3D10_REQ_MIP_LEVELS*/)
        {
            return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
        }

        // We could support a subset of 'DX10' extended header DDS files, but we'll assume here we are only
        // supporting legacy DDS files for a Direct3D9 device

        D3DFORMAT fmt = GetD3D9Format(header->ddspf);
        if (fmt == D3DFMT_UNKNOWN || BitsPerPixel(fmt) == 0)
        {
            return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
        }

        if (header->flags & DDS_HEADER_FLAGS_VOLUME)
        {
            UINT iDepth = header->depth;

            if ((iWidth > 2048u /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/)
                || (iHeight > 2048u /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/)
                || (iDepth > 2048u /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/))
            {
                return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
            }

            // Create the volume texture (let the runtime do the validation)
            ComPtr<IDirect3DVolumeTexture9> pTexture;
            hr = device->CreateVolumeTexture(iWidth, iHeight, iDepth, iMipCount,
                usage, fmt, pool, pTexture.GetAddressOf(), nullptr);
            if (FAILED(hr))
                return hr;

            ComPtr<IDirect3DVolumeTexture9> pStagingTexture;
            if (pool == D3DPOOL_DEFAULT)
            {
                hr = device->CreateVolumeTexture(iWidth, iHeight, iDepth, iMipCount,
                    0u, fmt, D3DPOOL_SYSTEMMEM, pStagingTexture.GetAddressOf(), nullptr);
                if (FAILED(hr))
                    return hr;
            }
            else
            {
                pStagingTexture = pTexture;
            }

            // Lock, fill, unlock
            size_t NumBytes = 0;
            size_t RowBytes = 0;
            size_t NumRows = 0;
            const uint8_t* pSrcBits = bitData;
            const uint8_t* pEndBits = bitData + bitSize;
            D3DLOCKED_BOX LockedBox = {};

            for (UINT i = 0; i < iMipCount; ++i)
            {
                GetSurfaceInfo(iWidth, iHeight, fmt, &NumBytes, &RowBytes, &NumRows);

                if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX)
                    return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);

                if ((pSrcBits + (NumBytes * iDepth)) > pEndBits)
                {
                    return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
                }

                if (SUCCEEDED(pStagingTexture->LockBox(i, &LockedBox, nullptr, 0)))
                {
                    auto pDestBits = static_cast<uint8_t*>(LockedBox.pBits);

                    for (UINT j = 0; j < iDepth; ++j)
                    {
                        uint8_t* dptr = pDestBits;
                        const uint8_t* sptr = pSrcBits;

                        // Copy stride line by line
                        for (size_t h = 0; h < NumRows; h++)
                        {
                            memcpy_s(dptr, static_cast<size_t>(LockedBox.RowPitch), sptr, RowBytes);
                            dptr += LockedBox.RowPitch;
                            sptr += RowBytes;
                        }

                        pDestBits += LockedBox.SlicePitch;
                        pSrcBits += NumBytes;
                    }

                    pStagingTexture->UnlockBox(i);
                }

                iWidth = iWidth >> 1;
                iHeight = iHeight >> 1;
                iDepth = iDepth >> 1;
                if (iWidth == 0)
                    iWidth = 1;
                if (iHeight == 0)
                    iHeight = 1;
                if (iDepth == 0)
                    iDepth = 1;
            }

            if (pool == D3DPOOL_DEFAULT)
            {
                hr = device->UpdateTexture(pStagingTexture.Get(), pTexture.Get());
                if (FAILED(hr))
                    return hr;
            }

            *texture = pTexture.Detach();
        }
        else if (header->caps2 & DDS_CUBEMAP)
        {
            if ((iWidth > 8192u /*D3D10_REQ_TEXTURECUBE_DIMENSION*/)
                || (iHeight > 8192u /*D3D10_REQ_TEXTURECUBE_DIMENSION*/))
            {
                return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
            }

            // We require at least one face to be defined, and the faces must be square
            if ((header->caps2 & DDS_CUBEMAP_ALLFACES) == 0 || iHeight != iWidth)
            {
                return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
            }

            // Create the cubemap (let the runtime do the validation)
            ComPtr<IDirect3DCubeTexture9> pTexture;
            hr = device->CreateCubeTexture(iWidth, iMipCount,
                usage, fmt, pool, pTexture.GetAddressOf(), nullptr);
            if (FAILED(hr))
                return hr;

            ComPtr<IDirect3DCubeTexture9> pStagingTexture;
            if (pool == D3DPOOL_DEFAULT)
            {
                hr = device->CreateCubeTexture(iWidth, iMipCount,
                    0u, fmt, D3DPOOL_SYSTEMMEM, pStagingTexture.GetAddressOf(), nullptr);
                if (FAILED(hr))
                    return hr;
            }
            else
            {
                pStagingTexture = pTexture;
            }

            // Lock, fill, unlock
            size_t NumBytes = 0;
            size_t RowBytes = 0;
            size_t NumRows = 0;
            const uint8_t* pSrcBits = bitData;
            const uint8_t* pEndBits = bitData + bitSize;
            D3DLOCKED_RECT LockedRect = {};

            UINT mask = DDS_CUBEMAP_POSITIVEX & ~DDS_CUBEMAP;
            for (UINT f = 0; f < 6; ++f, mask <<= 1)
            {
                if (!(header->caps2 & mask))
                    continue;

                UINT w = iWidth;
                UINT h = iHeight;
                for (UINT i = 0; i < iMipCount; ++i)
                {
                    GetSurfaceInfo(w, h, fmt, &NumBytes, &RowBytes, &NumRows);

                    if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX)
                        return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);

                    if ((pSrcBits + NumBytes) > pEndBits)
                    {
                        return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
                    }

                    if (SUCCEEDED(pStagingTexture->LockRect(static_cast<D3DCUBEMAP_FACES>(f), i, &LockedRect, nullptr, 0)))
                    {
                        auto pDestBits = static_cast<uint8_t*>(LockedRect.pBits);

                        // Copy stride line by line
                        for (size_t r = 0; r < NumRows; r++)
                        {
                            memcpy_s(pDestBits, static_cast<size_t>(LockedRect.Pitch), pSrcBits, RowBytes);
                            pDestBits += LockedRect.Pitch;
                            pSrcBits += RowBytes;
                        }

                        pStagingTexture->UnlockRect(static_cast<D3DCUBEMAP_FACES>(f), i);
                    }

                    w = w >> 1;
                    h = h >> 1;
                    if (w == 0)
                        w = 1;
                    if (h == 0)
                        h = 1;
                }
            }

            if (pool == D3DPOOL_DEFAULT)
            {
                hr = device->UpdateTexture(pStagingTexture.Get(), pTexture.Get());
                if (FAILED(hr))
                    return hr;
            }

            *texture = pTexture.Detach();
        }
        else
        {
            if ((iWidth > 8192u /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/)
                || (iHeight > 8192u /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/))
            {
                return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
            }

            // Create the texture (let the runtime do the validation)
            if (generateMipsIfMissing)
                usage |= D3DUSAGE_AUTOGENMIPMAP;

            ComPtr<IDirect3DTexture9> pTexture;
            hr = device->CreateTexture(iWidth, iHeight, iMipCount,
                usage, fmt, pool,
                pTexture.GetAddressOf(), nullptr);
            if (FAILED(hr))
                return hr;

            ComPtr<IDirect3DTexture9> pStagingTexture;
            if (pool == D3DPOOL_DEFAULT)
            {
                hr = device->CreateTexture(iWidth, iHeight, iMipCount,
                    0u, fmt, D3DPOOL_SYSTEMMEM, pStagingTexture.GetAddressOf(), nullptr);
                if (FAILED(hr))
                    return hr;
            }
            else
            {
                pStagingTexture = pTexture;
            }

            // Lock, fill, unlock
            size_t NumBytes = 0;
            size_t RowBytes = 0;
            size_t NumRows = 0;
            const uint8_t* pSrcBits = bitData;
            const uint8_t* pEndBits = bitData + bitSize;
            D3DLOCKED_RECT LockedRect = {};

            for (UINT i = 0; i < iMipCount; ++i)
            {
                GetSurfaceInfo(iWidth, iHeight, fmt, &NumBytes, &RowBytes, &NumRows);

                if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX)
                    return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);

                if ((pSrcBits + NumBytes) > pEndBits)
                {
                    return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
                }

                if (SUCCEEDED(pStagingTexture->LockRect(i, &LockedRect, nullptr, 0)))
                {
                    auto pDestBits = static_cast<uint8_t*>(LockedRect.pBits);

                    // Copy stride line by line
                    for (UINT h = 0; h < NumRows; h++)
                    {
                        memcpy_s(pDestBits, static_cast<size_t>(LockedRect.Pitch), pSrcBits, RowBytes);
                        pDestBits += LockedRect.Pitch;
                        pSrcBits += RowBytes;
                    }

                    pStagingTexture->UnlockRect(i);
                }

                iWidth = iWidth >> 1;
                iHeight = iHeight >> 1;
                if (iWidth == 0)
                    iWidth = 1;
                if (iHeight == 0)
                    iHeight = 1;
            }

            if (pool == D3DPOOL_DEFAULT)
            {
                hr = device->UpdateTexture(pStagingTexture.Get(), pTexture.Get());
                if (FAILED(hr))
                    return hr;
            }

            *texture = pTexture.Detach();
        }

        return hr;
    }