- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Examples

# ddsFloatReader.cpp

    //-
    // ==========================================================================
    // Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
    //
    // Use of this software is subject to the terms of the Autodesk
    // license agreement provided at the time of installation or download,
    // or which otherwise accompanies this software in either electronic
    // or hard copy form.
    // ==========================================================================
    //+

    // DESCRIPTION:
    //
    //  Example DDS floating point image reader plugin.
    //

    #include <maya/MPxImageFile.h>
    #include <maya/MImageFileInfo.h>
    #include <maya/MImage.h>
    #include <maya/MFnPlugin.h>
    #include <maya/MStringArray.h>
    #include <maya/MIOStream.h>

    #if _WIN32
    #pragma warning( disable : 4290 )       // Disable STL warnings.
    #endif

    #include "ddsFloatReader.h"
    #include <math.h>

    using namespace dds_Float_Reader;
    MString kImageFormatName( "DDS Float");

    class ddsFloatReader : public MPxImageFile
    {
    public:
                        ddsFloatReader();
        virtual         ~ddsFloatReader();
        static void*    creator();
        virtual MStatus open( MString pathname, MImageFileInfo* info);
        virtual MStatus load( MImage& image, unsigned int idx);
        virtual MStatus close();

    protected:
        // Data members
        unsigned int        fWidth;
        unsigned int        fHeight;
        unsigned int        fNumChannels;
        unsigned int        fBytesPerPixel;

        // File and header description
        FILE                *fInputFile;
        DDS_HEADER          fHeader;
    };

    //
    // DESCRIPTION:
    ddsFloatReader::ddsFloatReader()
    :   fWidth(0),
        fHeight(0),
        fNumChannels(0),
        fBytesPerPixel(0),
        fInputFile(NULL)
    {
    }

    //
    // DESCRIPTION:
    ddsFloatReader::~ddsFloatReader()
    {
        close();
    }

    //
    // DESCRIPTION:
    /* virtual */
    MStatus  ddsFloatReader::close()
    {
        fWidth = 0;
        fHeight = 0;
        fNumChannels = 0;
        fBytesPerPixel = 0;

        // Close our file
        if (fInputFile != NULL)
            fclose(fInputFile);
        fInputFile = NULL;

        return MS::kSuccess;
    }

    //
    // DESCRIPTION:
    void * ddsFloatReader::creator()
    {
        return new ddsFloatReader();
    }

    // Need to swap byte order on Mac
    //
    inline void swap_endian(void *val)
    {
    #if defined(OSMac_)
        unsigned int *ival = (unsigned int *)val;

        *ival = ((*ival >> 24) & 0x000000ff) |
                ((*ival >>  8) & 0x0000ff00) |
                ((*ival <<  8) & 0x00ff0000) |
                ((*ival << 24) & 0xff000000);

    #endif
    }

    inline void swap_endian_half(void *val)
    {
    #if defined(OSMac_)
        unsigned short *ival = (unsigned short *)val;

        *ival = ((*ival >> 8) & 255) |
                ((*ival << 8) & 65280);

    #endif
    }

    const double two_pow_neg14 = pow(2.0, -14.0);

    //
    // Half to float conversion
    //
    inline float halfToFloat(unsigned short val)
    {
        float outValue = 0.0f;

    #if defined(OSMac_)
        // Need to swap bytes on Power PC (Mac)
        swap_endian_half( &val );
    #endif

        // Convert 16-value into a float...
        double h_mantissa = (float) (val & 1023); // Mantissa = low order 10  bits
        double h_exponent = (float)  ((val >> 10) & 31); // Exponent = next 5 bits
        unsigned int i_sign = (val >> 15) & 1;  ; // Sign is the highest bit
        double h_sign = (i_sign == 0) ? 1.0 : -1.0;

        if (h_exponent != 30.0)
        {
            outValue = (float) (h_sign * pow(2.0, h_exponent-15.0) * ( 1.0 + ( h_mantissa / 1024.0 )));
        }
        else
        {
            outValue = (float) ( h_sign * pow(2.0, two_pow_neg14) * ( h_mantissa / 1024.0 ) );
        }
        //printf("[%d][%d] = inValue=%d, isign =%d, sign=%g, exp=%g, mant=%g, value=%g\n", y,x, *inPtr, i_sign, h_sign, h_exponent, h_mantissa, outValue);
        return outValue;
    }

    //
    // DESCRIPTION:
    MStatus ddsFloatReader::open( MString filename, MImageFileInfo* info)
    {
    #if _WIN32
        if ( ( fInputFile = fopen( filename.asChar(), "rb" ) ) == NULL )
    #else
        if ( ( fInputFile = fopen( filename.asChar(), "r" ) ) == NULL )
    #endif
        {
            // Unable to open the file
            return MS::kFailure;
        }

        // Read the DDS header in
        //
        if ( fread( &fHeader, 1, sizeof(DDS_HEADER), fInputFile) == sizeof(DDS_HEADER) )
        {
            swap_endian(&fHeader.fCapabilities.dwCaps2);

            // Cube maps and volume textures are not supported currently.
            //
            if (( (fHeader.fCapabilities.dwCaps2) & DDSCAPS2_CUBEMAP_FLAG ) ||
                ( (fHeader.fCapabilities.dwCaps2) & DDSCAPS2_VOLUME_FLAG ))
            {
                close();
                return MS::kFailure;
            }

            // Get dimensions of image
            //
            swap_endian(&fHeader.fWidth);
            swap_endian(&fHeader.fHeight);
            fWidth = fHeader.fWidth;
            fHeight = fHeader.fHeight;
            fNumChannels = 0;

            if (fWidth==0 || fHeight==0)
            {
                close();
                return MS::kFailure;
            }

            // Check for float formats
            //
            swap_endian(& fHeader.fFormat.fFlags );
            if (fHeader.fFormat.fFlags & DDS_FOURCC_FLAG)
            {
                swap_endian(&fHeader.fFormat.fPixelFormat);

                bool supportedFormat = true;

                // Half float formats
                switch (fHeader.fFormat.fPixelFormat)
                {

                // Half float formats
                //
                case DDS_R16F:
                    fNumChannels = 1;
                    fBytesPerPixel = 2; // 16-bits
                    break;
                case DDS_G16R16F:
                    fNumChannels = 2;
                    fBytesPerPixel = 4; // 32-bits
                    break;
                case DDS_A16B16G16R16F:
                    fNumChannels = 4;
                    fBytesPerPixel = 8; // 64-bits
                    break;

                // IEEE 32 bit formats
                case DDS_R32F:
                    fNumChannels = 1;
                    fBytesPerPixel = 4; // 32-bits
                    break;
                case DDS_G32R32F:
                    fNumChannels = 2;
                    fBytesPerPixel = 8; // 64-bits
                    break;
                case DDS_A32B32G32R32F:
                    fNumChannels = 4;
                    fBytesPerPixel = 16; // 128-bits
                    break;

                // Anything else is not supported
                default:
                    supportedFormat = false;
                    break;
                }

                if (!supportedFormat)
                {
                    close();
                    return MS::kFailure;
                }
            }
            else
            {
                close();
                return MS::kFailure;
            }

            // Return image information based on the header
            //
            if (info)
            {
                //printf("** Opened DDS: %s (w=%d,h=%d, fmt=%d)\n",
                //  filename.asChar(),
                //  fWidth,
                //  fHeight,
                //  fHeader.fFormat.fPixelFormat);

                // Only read in 1 image for now
                info->numberOfImages( 1 );
                info->width( fWidth );
                info->height( fHeight );
                info->channels( fNumChannels );
                info->hasAlpha( fNumChannels == 4 );

                // Mip maps not handled for now
                info->hasMipMaps( false );

                info->imageType( MImageFileInfo::kImageTypeColor );
                info->hardwareType( MImageFileInfo::kHwTexture2D );
                info->pixelType( MImage::kFloat );
            }
        }
        return MS::kSuccess;
    }

    //
    // DESCRIPTION:
    MStatus ddsFloatReader::load( MImage& image, unsigned int imageNumber)
    {
        MStatus loaded = MS::kFailure;

        // Check for half type, since we need to perform
        // some special bit conversions to get a float back.
        //
        bool isHalfType = false;
        switch (fHeader.fFormat.fPixelFormat)
        {
        case DDS_R16F:
            isHalfType = true;
            break;
        case DDS_G16R16F:
            isHalfType = true;
            break;
        case DDS_A16B16G16R16F:
            isHalfType = true;
            break;
        default:
            break;
        }

        // Create the output buffer
        //
        image.create( fWidth, fHeight, fNumChannels, MImage::kFloat);
        float* outputBuffer = image.floatPixels();

        // Create the input buffer to read one scanline at a time.
        //
        float *inputFloatBuffer = NULL;         // For IEEE float
        unsigned short *inputIntBuffer = NULL;  // For half-float = 16-bits per channel
        if (!isHalfType)
        {
            inputFloatBuffer = new float[fWidth * fNumChannels];
        }
        else
            inputIntBuffer= new unsigned short[fWidth * fNumChannels];

        // Can't get sufficient memory so fail and close up the
        // file.
        if ((!inputFloatBuffer && !inputIntBuffer) || !outputBuffer)
        {
            if (inputFloatBuffer)
            {
                delete [] inputFloatBuffer;
                inputFloatBuffer = NULL;
            }
            if (inputIntBuffer)
            {
                delete [] inputIntBuffer;
                inputIntBuffer = NULL;
            }
            close();
            return MS::kFailure;
        }

        // Transfer to output buffer.
        //
        if (isHalfType)
        {
            unsigned int x,y;
            float *outPtr = outputBuffer;

            // Do a scanline at a time. From top-to-bottom
            // so that scan lines are flipped for Maya's usage.
            //
            outPtr += (fHeight-1) * (fWidth * fNumChannels);

            for (y=0; y<fHeight; y++)
            {
                // Read in one scan line of data
                unsigned int numBytes = fWidth * fBytesPerPixel;
                fread( inputIntBuffer, 1, numBytes, fInputFile);

                unsigned short *inPtr = inputIntBuffer;

                for (x=0; x<fWidth * fNumChannels; x++)
                {
                    float outValue = halfToFloat(*inPtr);
                    *(outPtr + x) = outValue;
                    inPtr++; // Jump 2 bytes at a time = 1 channel
                }
                outPtr -= (fWidth * fNumChannels);
            }
            loaded = MS::kSuccess;
        }

        // IEEE 32-bit float
        else
        {
            unsigned int x,y;
            float *outPtr = outputBuffer;

            // Do a scanline at a time. From top-to-bottom
            // so that scan lines are flipped for Maya's usage.
            //
            outPtr += (fHeight-1) * (fWidth * fNumChannels);

            for (y=0; y<fHeight; y++)
            {
                // Read in one scan line of data
                unsigned int numBytes = fWidth * fBytesPerPixel;
                fread( inputFloatBuffer, 1, numBytes, fInputFile);

    #if defined(OSMac_)
                // Need to swap bytes on Power PC (Mac)
                unsigned int *bPtr = (unsigned int *)inputFloatBuffer;
                unsigned int b;
                for (b=0; b<numBytes / 4; b++)
                {
                    swap_endian( bPtr );
                    bPtr++;
                }
    #endif
                float *inPtr = inputFloatBuffer;
                for (x=0; x<fWidth * fNumChannels; x++)
                {
                    *(outPtr + x) = *inPtr;
                    inPtr++;
                }
                outPtr -= (fWidth * fNumChannels);
            }
            loaded = MS::kSuccess;
        }

        // Cleanup memory and close the file
        if (inputFloatBuffer)
        {
            delete [] inputFloatBuffer;
            inputFloatBuffer = NULL;
        }
        if (inputIntBuffer)
        {
            delete [] inputFloatBuffer;
            inputFloatBuffer = NULL;
        }
        close();

        return loaded;
    }

    MStatus initializePlugin( MObject obj )
    {
        MFnPlugin plugin( obj, PLUGIN_COMPANY, "8.0", "Any" );
        MStringArray extensions;
        extensions.append( "dds");
        CHECK_MSTATUS( plugin.registerImageFile(
                        kImageFormatName,
                        ddsFloatReader::creator,
                        extensions));

        return MS::kSuccess;
    }

    // DESCRIPTION:
    MStatus uninitializePlugin( MObject obj )
    {
        MFnPlugin plugin( obj );
        CHECK_MSTATUS( plugin.deregisterImageFile( kImageFormatName ) );

        return MS::kSuccess;
    }