DdsFile.cs :  » GUI » Paint.net » DdsFileTypePlugin » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » GUI » Paint.net 
Paint.net » DdsFileTypePlugin » DdsFile.cs
//------------------------------------------------------------------------------
/*
  @brief    DDS File Type Plugin for Paint.NET

  @note    Copyright (c) 2007 Dean Ashton         http://www.dmashton.co.uk

  Permission is hereby granted, free of charge, to any person obtaining
  a copy of this software and associated documentation files (the 
  "Software"), to  deal in the Software without restriction, including
  without limitation the rights to use, copy, modify, merge, publish,
  distribute, sublicense, and/or sell copies of the Software, and to 
  permit persons to whom the Software is furnished to do so, subject to 
  the following conditions:

  The above copyright notice and this permission notice shall be included
  in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**/
//------------------------------------------------------------------------------

// If we want to do the alignment as per the (broken) DDS documentation, then we
// uncomment this define.. 
//#define  APPLY_PITCH_ALIGNMENT

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using PaintDotNet;
using System.Drawing;

namespace DdsFileTypePlugin{
  public enum DdsFileFormat
  {
    DDS_FORMAT_DXT1,
    DDS_FORMAT_DXT3,
    DDS_FORMAT_DXT5,
    DDS_FORMAT_A8R8G8B8,
    DDS_FORMAT_X8R8G8B8,
    DDS_FORMAT_A8B8G8R8,
    DDS_FORMAT_X8B8G8R8,
    DDS_FORMAT_A1R5G5B5,
    DDS_FORMAT_A4R4G4B4,
    DDS_FORMAT_R8G8B8,
    DDS_FORMAT_R5G6B5,

    DDS_FORMAT_INVALID,
  };

  public class DdsPixelFormat
  {
    public enum PixelFormatFlags
    {
      DDS_FOURCC  =  0x00000004,
      DDS_RGB    =  0x00000040,
      DDS_RGBA  =  0x00000041,
    }

      public uint  m_size;
      public uint  m_flags;
      public uint  m_fourCC;
      public uint  m_rgbBitCount;
      public uint  m_rBitMask;
      public uint  m_gBitMask;
      public uint  m_bBitMask;
      public uint  m_aBitMask;

    public uint  Size()
    {
      return 8 * 4;
    }

    public void Initialise( DdsFileFormat fileFormat )
    {
      m_size = Size();
      switch( fileFormat )
      {
        case  DdsFileFormat.DDS_FORMAT_DXT1:
        case  DdsFileFormat.DDS_FORMAT_DXT3:
        case  DdsFileFormat.DDS_FORMAT_DXT5:
        {
          // DXT1/DXT3/DXT5
          m_flags      = ( int )PixelFormatFlags.DDS_FOURCC;
          m_rgbBitCount  =  0;
          m_rBitMask    =  0;
          m_gBitMask    =  0;
          m_bBitMask    =  0;
          m_aBitMask    =  0;
          if ( fileFormat == DdsFileFormat.DDS_FORMAT_DXT1 ) m_fourCC = 0x31545844;  //"DXT1"
          if ( fileFormat == DdsFileFormat.DDS_FORMAT_DXT3 ) m_fourCC = 0x33545844;  //"DXT1"
          if ( fileFormat == DdsFileFormat.DDS_FORMAT_DXT5 ) m_fourCC = 0x35545844;  //"DXT1"
          break;
        }
  
        case  DdsFileFormat.DDS_FORMAT_A8R8G8B8:
        {  
          m_flags      = ( int )PixelFormatFlags.DDS_RGBA;
          m_rgbBitCount  = 32;
          m_fourCC    = 0;
          m_rBitMask    = 0x00ff0000;
          m_gBitMask    = 0x0000ff00;
          m_bBitMask    = 0x000000ff;
          m_aBitMask    = 0xff000000;
          break;
        }

        case  DdsFileFormat.DDS_FORMAT_X8R8G8B8:
        {  
          m_flags      = ( int )PixelFormatFlags.DDS_RGB;
          m_rgbBitCount  = 32;
          m_fourCC    = 0;
          m_rBitMask    = 0x00ff0000;
          m_gBitMask    = 0x0000ff00;
          m_bBitMask    = 0x000000ff;
          m_aBitMask    = 0x00000000;
          break;
        }

        case  DdsFileFormat.DDS_FORMAT_A8B8G8R8:
        {  
          m_flags      = ( int )PixelFormatFlags.DDS_RGBA;
          m_rgbBitCount  = 32;
          m_fourCC    = 0;
          m_rBitMask    = 0x000000ff;
          m_gBitMask    = 0x0000ff00;
          m_bBitMask    = 0x00ff0000;
          m_aBitMask    = 0xff000000;
          break;
        }

        case  DdsFileFormat.DDS_FORMAT_X8B8G8R8:
        {  
          m_flags      = ( int )PixelFormatFlags.DDS_RGB;
          m_rgbBitCount  = 32;
          m_fourCC    = 0;
          m_rBitMask    = 0x000000ff;
          m_gBitMask    = 0x0000ff00;
          m_bBitMask    = 0x00ff0000;
          m_aBitMask    = 0x00000000;
          break;
        }

        case  DdsFileFormat.DDS_FORMAT_A1R5G5B5:
        {  
          m_flags      = ( int )PixelFormatFlags.DDS_RGBA;
          m_rgbBitCount  = 16;
          m_fourCC    = 0;
          m_rBitMask    = 0x00007c00;
          m_gBitMask    = 0x000003e0;
          m_bBitMask    = 0x0000001f;
          m_aBitMask    = 0x00008000;
          break;
        }

        case  DdsFileFormat.DDS_FORMAT_A4R4G4B4:
        {  
          m_flags      = ( int )PixelFormatFlags.DDS_RGBA;
          m_rgbBitCount  = 16;
          m_fourCC    = 0;
          m_rBitMask    = 0x00000f00;
          m_gBitMask    = 0x000000f0;
          m_bBitMask    = 0x0000000f;
          m_aBitMask    = 0x0000f000;
          break;
        }

        case  DdsFileFormat.DDS_FORMAT_R8G8B8:
        {  
          m_flags      = ( int )PixelFormatFlags.DDS_RGB;
          m_fourCC    = 0;
          m_rgbBitCount  = 24;
          m_rBitMask    = 0x00ff0000;
          m_gBitMask    = 0x0000ff00;
          m_bBitMask    = 0x000000ff;
          m_aBitMask    = 0x00000000;
          break;
        }

        case  DdsFileFormat.DDS_FORMAT_R5G6B5:
        {  
          m_flags      = ( int )PixelFormatFlags.DDS_RGB;
          m_fourCC    = 0;
          m_rgbBitCount  = 16;
          m_rBitMask    = 0x0000f800;
          m_gBitMask    = 0x000007e0;
          m_bBitMask    = 0x0000001f;
          m_aBitMask    = 0x00000000;
          break;
        }
    
        default:
          break;
      }
    }

    public void Read( System.IO.Stream input )
    {
      this.m_size      = ( uint )Utility.ReadUInt32( input );
        this.m_flags    = ( uint )Utility.ReadUInt32( input );
        this.m_fourCC    = ( uint )Utility.ReadUInt32( input );
        this.m_rgbBitCount  = ( uint )Utility.ReadUInt32( input );
        this.m_rBitMask    = ( uint )Utility.ReadUInt32( input );
        this.m_gBitMask    = ( uint )Utility.ReadUInt32( input );
        this.m_bBitMask    = ( uint )Utility.ReadUInt32( input );
        this.m_aBitMask    = ( uint )Utility.ReadUInt32( input );
    }

    public void Write( System.IO.Stream output )
    {
      Utility.WriteUInt32( output, this.m_size );
      Utility.WriteUInt32( output, this.m_flags );
      Utility.WriteUInt32( output, this.m_fourCC );
      Utility.WriteUInt32( output, this.m_rgbBitCount );
      Utility.WriteUInt32( output, this.m_rBitMask );
      Utility.WriteUInt32( output, this.m_gBitMask );
      Utility.WriteUInt32( output, this.m_bBitMask );
      Utility.WriteUInt32( output, this.m_aBitMask );
    }
  }

  public class DdsHeader
  {
    public enum HeaderFlags
    {
      DDS_HEADER_FLAGS_TEXTURE  =  0x00001007,  // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT 
      DDS_HEADER_FLAGS_MIPMAP    =  0x00020000,  // DDSD_MIPMAPCOUNT
      DDS_HEADER_FLAGS_VOLUME    =  0x00800000,  // DDSD_DEPTH
      DDS_HEADER_FLAGS_PITCH    =  0x00000008,  // DDSD_PITCH
      DDS_HEADER_FLAGS_LINEARSIZE  =  0x00080000,  // DDSD_LINEARSIZE
    }

    public enum SurfaceFlags
    {
      DDS_SURFACE_FLAGS_TEXTURE  =  0x00001000,  // DDSCAPS_TEXTURE
      DDS_SURFACE_FLAGS_MIPMAP  =  0x00400008,  // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP
      DDS_SURFACE_FLAGS_CUBEMAP  =  0x00000008,  // DDSCAPS_COMPLEX
    }

    public enum CubemapFlags
    {
      DDS_CUBEMAP_POSITIVEX    =  0x00000600, // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
      DDS_CUBEMAP_NEGATIVEX    =  0x00000a00, // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
      DDS_CUBEMAP_POSITIVEY    =  0x00001200, // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
      DDS_CUBEMAP_NEGATIVEY    =  0x00002200, // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
      DDS_CUBEMAP_POSITIVEZ    =  0x00004200, // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
      DDS_CUBEMAP_NEGATIVEZ    =  0x00008200, // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ
    
      DDS_CUBEMAP_ALLFACES    =  (  DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |
                        DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |
                        DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ )
    }

    public enum VolumeFlags
    {
      DDS_FLAGS_VOLUME      =  0x00200000,  // DDSCAPS2_VOLUME
    }

    public DdsHeader()
    {
      m_pixelFormat  = new DdsPixelFormat();
    }

    public uint  Size()
    {
      return ( 18 * 4 ) + m_pixelFormat.Size() + ( 5 * 4 );
    }

    public uint        m_size;
    public uint        m_headerFlags;
    public uint        m_height;
    public uint        m_width;
    public uint        m_pitchOrLinearSize;
    public uint        m_depth;
    public uint        m_mipMapCount;
    public uint        m_reserved1_0;
    public uint        m_reserved1_1;
    public uint        m_reserved1_2;
    public uint        m_reserved1_3;
    public uint        m_reserved1_4;
    public uint        m_reserved1_5;
    public uint        m_reserved1_6;
    public uint        m_reserved1_7;
    public uint        m_reserved1_8;
    public uint        m_reserved1_9;
    public uint        m_reserved1_10;
    public DdsPixelFormat  m_pixelFormat;
    public uint        m_surfaceFlags;
    public uint        m_cubemapFlags;
    public uint        m_reserved2_0;
    public uint        m_reserved2_1;
    public uint        m_reserved2_2;

    public void Read( System.IO.Stream input )
    {
      this.m_size          = ( uint )Utility.ReadUInt32( input );
        this.m_headerFlags      = ( uint )Utility.ReadUInt32( input );
        this.m_height        = ( uint )Utility.ReadUInt32( input );
        this.m_width        = ( uint )Utility.ReadUInt32( input );
        this.m_pitchOrLinearSize  = ( uint )Utility.ReadUInt32( input );
        this.m_depth        = ( uint )Utility.ReadUInt32( input );
        this.m_mipMapCount      = ( uint )Utility.ReadUInt32( input );
        this.m_reserved1_0      = ( uint )Utility.ReadUInt32( input );
        this.m_reserved1_1      = ( uint )Utility.ReadUInt32( input );
        this.m_reserved1_2      = ( uint )Utility.ReadUInt32( input );
        this.m_reserved1_3      = ( uint )Utility.ReadUInt32( input );
        this.m_reserved1_4      = ( uint )Utility.ReadUInt32( input );
        this.m_reserved1_5      = ( uint )Utility.ReadUInt32( input );
        this.m_reserved1_6      = ( uint )Utility.ReadUInt32( input );
        this.m_reserved1_7      = ( uint )Utility.ReadUInt32( input );
        this.m_reserved1_8      = ( uint )Utility.ReadUInt32( input );
        this.m_reserved1_9      = ( uint )Utility.ReadUInt32( input );
        this.m_reserved1_10      = ( uint )Utility.ReadUInt32( input );
      this.m_pixelFormat.Read( input );
      this.m_surfaceFlags      = ( uint )Utility.ReadUInt32( input );
      this.m_cubemapFlags      = ( uint )Utility.ReadUInt32( input );
      this.m_reserved2_0      = ( uint )Utility.ReadUInt32( input );
      this.m_reserved2_1      = ( uint )Utility.ReadUInt32( input );
      this.m_reserved2_2      = ( uint )Utility.ReadUInt32( input );
    }

    public void Write( System.IO.Stream output )
    {
      Utility.WriteUInt32( output, this.m_size );
      Utility.WriteUInt32( output, this.m_headerFlags );
      Utility.WriteUInt32( output, this.m_height );
      Utility.WriteUInt32( output, this.m_width );
      Utility.WriteUInt32( output, this.m_pitchOrLinearSize );
      Utility.WriteUInt32( output, this.m_depth );
      Utility.WriteUInt32( output, this.m_mipMapCount );
      Utility.WriteUInt32( output, this.m_reserved1_0 );
      Utility.WriteUInt32( output, this.m_reserved1_1 );
      Utility.WriteUInt32( output, this.m_reserved1_2 );
      Utility.WriteUInt32( output, this.m_reserved1_3 );
      Utility.WriteUInt32( output, this.m_reserved1_4 );
      Utility.WriteUInt32( output, this.m_reserved1_5 );
      Utility.WriteUInt32( output, this.m_reserved1_6 );
      Utility.WriteUInt32( output, this.m_reserved1_7 );
      Utility.WriteUInt32( output, this.m_reserved1_8 );
      Utility.WriteUInt32( output, this.m_reserved1_9 );
      Utility.WriteUInt32( output, this.m_reserved1_10 );
      this.m_pixelFormat.Write( output );
      Utility.WriteUInt32( output, this.m_surfaceFlags );
      Utility.WriteUInt32( output, this.m_cubemapFlags );
      Utility.WriteUInt32( output, this.m_reserved2_0 );
      Utility.WriteUInt32( output, this.m_reserved2_1 );
      Utility.WriteUInt32( output, this.m_reserved2_2 );
    }

  }  

  public  class DdsFile
  {
    public  DdsFile()
    {
      m_header = new DdsHeader();
    }

    public  void  Save( System.IO.Stream output, Surface surface, DdsSaveConfigToken ddsToken, ProgressEventHandler progressCallback )
    {
      // For non-compressed textures, we need pixel width.
      int pixelWidth  = 0;

      // Identify if we're a compressed image
      bool isCompressed = (  ( ddsToken.m_fileFormat == DdsFileFormat.DDS_FORMAT_DXT1 ) || 
                  ( ddsToken.m_fileFormat == DdsFileFormat.DDS_FORMAT_DXT3 ) ||
                  ( ddsToken.m_fileFormat == DdsFileFormat.DDS_FORMAT_DXT5 ) );

      // Compute mip map count..
      int  mipCount  = 1;
      int  mipWidth  = surface.Width;
      int  mipHeight  = surface.Height;

      if ( ddsToken.m_generateMipMaps )
      {
        // This breaks!

        while ( ( mipWidth > 1 ) || ( mipHeight > 1 ) )
        {
          mipCount++;
          mipWidth  /= 2;
          mipHeight /= 2;
        }
      }

      // Populate bulk of our DdsHeader
      m_header.m_size          =  m_header.Size();
      m_header.m_headerFlags      =  ( uint )( DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_TEXTURE );

      if ( isCompressed )
        m_header.m_headerFlags    |=  ( uint )( DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_LINEARSIZE );
      else
        m_header.m_headerFlags    |=  ( uint )( DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_PITCH );

      if ( mipCount > 1 )
        m_header.m_headerFlags    |=  ( uint )( DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_MIPMAP );

      m_header.m_height        =  ( uint )surface.Height;
      m_header.m_width        =  ( uint )surface.Width;

      if ( isCompressed )
      {
        // Compresssed textures have the linear flag set.So pitchOrLinearSize
        // needs to contain the entire size of the DXT block.
        int blockCount = ( ( surface.Width + 3 )/4 ) * ( ( surface.Height + 3 )/4 );
        int blockSize = ( ddsToken.m_fileFormat == 0 ) ? 8 : 16;
        m_header.m_pitchOrLinearSize =  ( uint )( blockCount * blockSize );
      }
      else
      {
        // Non-compressed textures have the pitch flag set. So pitchOrLinearSize
        // needs to contain the row pitch of the main image. DWORD aligned too.
        switch ( ddsToken.m_fileFormat )
        {
          case  DdsFileFormat.DDS_FORMAT_A8R8G8B8:
          case  DdsFileFormat.DDS_FORMAT_X8R8G8B8:
          case  DdsFileFormat.DDS_FORMAT_A8B8G8R8:
          case  DdsFileFormat.DDS_FORMAT_X8B8G8R8:
            pixelWidth = 4;    // 32bpp
            break;
    
          case  DdsFileFormat.DDS_FORMAT_A1R5G5B5:
          case  DdsFileFormat.DDS_FORMAT_A4R4G4B4:
          case  DdsFileFormat.DDS_FORMAT_R5G6B5:
            pixelWidth = 2;    // 16bpp
            break;
  
          case  DdsFileFormat.DDS_FORMAT_R8G8B8:
            pixelWidth = 3;    // 24bpp
            break;
        }
    
        // Compute row pitch
        m_header.m_pitchOrLinearSize = ( uint )( ( int )m_header.m_width * pixelWidth );

#if  APPLY_PITCH_ALIGNMENT
        // Align to DWORD, if we need to.. (see notes about pitch alignment all over this code)
        m_header.m_pitchOrLinearSize = ( uint )( ( ( int )m_header.m_pitchOrLinearSize + 3 ) & ( ~3 ) );
#endif  //APPLY_PITCH_ALIGNMENT
      }
          
      m_header.m_depth        =  0;
      m_header.m_mipMapCount      =  ( mipCount == 1 ) ? 0 : ( uint )mipCount;
      m_header.m_reserved1_0      =  0;
      m_header.m_reserved1_1      =  0;
      m_header.m_reserved1_2      =  0;
      m_header.m_reserved1_3      =  0;
      m_header.m_reserved1_4      =  0;
      m_header.m_reserved1_5      =  0;
      m_header.m_reserved1_6      =  0;
      m_header.m_reserved1_7      =  0;
      m_header.m_reserved1_8      =  0;
      m_header.m_reserved1_9      =  0;
      m_header.m_reserved1_10      =  0;

      // Populate our DdsPixelFormat object
      m_header.m_pixelFormat.Initialise( ddsToken.m_fileFormat );

      // Populate miscellanous header flags
      m_header.m_surfaceFlags    =  ( uint )DdsHeader.SurfaceFlags.DDS_SURFACE_FLAGS_TEXTURE;

      if ( mipCount > 1 )
        m_header.m_surfaceFlags  |=  ( uint )DdsHeader.SurfaceFlags.DDS_SURFACE_FLAGS_MIPMAP;

      m_header.m_cubemapFlags    =  0;
      m_header.m_reserved2_0    =  0;
      m_header.m_reserved2_1    =  0;
      m_header.m_reserved2_2    =  0;

      // Write out our DDS tag
      Utility.WriteUInt32( output, 0x20534444 ); // 'DDS '

      // Write out the header
      m_header.Write( output );

      int  squishFlags = ddsToken.GetSquishFlags();
    
      // Our output data array will be sized as necessary
      byte[]  outputData;

      // Reset our mip width & height variables...
      mipWidth  = surface.Width;
      mipHeight  = surface.Height;

            // Figure out how much total work each mip map is
            Size[] writeSizes = new Size[mipCount];
            int[] mipPixels = new int[mipCount];
            int[] pixelsCompleted = new int[mipCount]; // # pixels completed once we have reached this mip
            long totalPixels = 0;
            for (int mipLoop = 0; mipLoop < mipCount; mipLoop++)
            {
                Size writeSize = new Size((mipWidth > 0) ? mipWidth : 1, (mipHeight > 0) ? mipHeight : 1);
                writeSizes[mipLoop] = writeSize;

                int thisMipPixels = writeSize.Width * writeSize.Height;
                mipPixels[mipLoop] = thisMipPixels;

                if (mipLoop == 0)
                {
                    pixelsCompleted[mipLoop] = 0;
                }
                else
                {
                    pixelsCompleted[mipLoop] = pixelsCompleted[mipLoop - 1] + mipPixels[mipLoop - 1];
                }

                totalPixels += thisMipPixels;
                mipWidth /= 2;
                mipHeight /= 2;
            }

            mipWidth = surface.Width;
            mipHeight = surface.Height;

            for (int mipLoop = 0; mipLoop < mipCount; mipLoop++)
      {
                Size writeSize = writeSizes[mipLoop];
        Surface  writeSurface = new Surface(writeSize);

        if ( mipLoop == 0 )
        {
          // No point resampling the first level.. it's got exactly what we want.
          writeSurface = surface;
        }
        else
        {
          // I'd love to have a UI component to select what kind of resampling, but
          // there's hardly any space for custom UI stuff in the Save Dialog. And I'm
          // not having any scrollbars in there..! 
          // Also, note that each mip level is formed from the main level, to reduce
          // compounded errors when generating mips. 
          writeSurface.SuperSamplingFitSurface( surface );
                }

                DdsSquish.ProgressFn progressFn =
                    delegate(int workDone, int workTotal)
                    {
                        long thisMipPixelsDone = workDone * (long)mipWidth;
                        long previousMipsPixelsDone = pixelsCompleted[mipLoop];
                        double progress = (double)((double)thisMipPixelsDone + (double)previousMipsPixelsDone) / (double)totalPixels;
                        progressCallback(this, new ProgressEventArgs(100.0 * progress));
                    };

        if ( ( ddsToken.m_fileFormat >= DdsFileFormat.DDS_FORMAT_DXT1 ) && ( ddsToken.m_fileFormat <= DdsFileFormat.DDS_FORMAT_DXT5 ) )
          outputData = DdsSquish.CompressImage( writeSurface, squishFlags, (progressCallback == null) ? null : progressFn );
        else
        {
          int  mipPitch = pixelWidth * writeSurface.Width;

          // From the DDS documents I read, I'd expected the pitch of each mip level to be
          // DWORD aligned. As it happens, that's not the case. Re-aligning the pitch of 
          // each level results in later mips getting sheared as the pitch is incorrect.
          // So, the following line is intentionally optional. Maybe the documentation
          // is referring to the pitch when accessing the mip directly.. who knows. 
          //
          // Infact, all the talk of non-compressed textures having DWORD alignment of pitch
          // seems to be bollocks.. If I apply alignment, then they fail to load in 3rd Party
          // or Microsoft DDS viewing applications.
          //

#if  APPLY_PITCH_ALIGNMENT
          mipPitch = ( mipPitch + 3 ) & ( ~3 );
#endif // APPLY_PITCH_ALIGNMENT

          outputData = new byte[ mipPitch * writeSurface.Height ];
          outputData.Initialize();

          for ( int y = 0; y < writeSurface.Height; y++ )
          {
            for ( int x = 0; x < writeSurface.Width; x++ )
            {
              // Get colour from surface
              ColorBgra  pixelColour = writeSurface.GetPoint( x, y );
              uint    pixelData  = 0;
          
              switch( ddsToken.m_fileFormat )
              {
                case  DdsFileFormat.DDS_FORMAT_A8R8G8B8:
                {
                  pixelData = ( ( uint )pixelColour.A << 24 ) | 
                        ( ( uint )pixelColour.R << 16 ) |
                        ( ( uint )pixelColour.G <<  8 ) |
                        ( ( uint )pixelColour.B <<  0 );
                  break;
                }

                case  DdsFileFormat.DDS_FORMAT_X8R8G8B8:
                {
                  pixelData = ( ( uint )pixelColour.R << 16 ) |
                        ( ( uint )pixelColour.G <<  8 ) |
                        ( ( uint )pixelColour.B <<  0 );
                  break;
                }

                case  DdsFileFormat.DDS_FORMAT_A8B8G8R8:
                {
                  pixelData = ( ( uint )pixelColour.A << 24 ) | 
                        ( ( uint )pixelColour.B << 16 ) |
                        ( ( uint )pixelColour.G <<  8 ) |
                        ( ( uint )pixelColour.R <<  0 );
                  break;
                }

                case  DdsFileFormat.DDS_FORMAT_X8B8G8R8:
                {
                  pixelData = ( ( uint )pixelColour.B << 16 ) |
                        ( ( uint )pixelColour.G <<  8 ) |
                        ( ( uint )pixelColour.R <<  0 );
                  break;
                }
                
                case  DdsFileFormat.DDS_FORMAT_A1R5G5B5:
                {
                  pixelData = ( ( uint )( ( pixelColour.A != 0 ) ? 1 : 0 ) << 15 ) |
                        ( ( uint )( pixelColour.R >> 3 ) << 10 ) |
                        ( ( uint )( pixelColour.G >> 3 ) <<  5 ) |
                        ( ( uint )( pixelColour.B >> 3 ) <<  0 );
                  break;
                }

                case  DdsFileFormat.DDS_FORMAT_A4R4G4B4:
                {
                  pixelData = ( ( uint )( pixelColour.A >> 4 ) << 12 ) | 
                        ( ( uint )( pixelColour.R >> 4 ) <<  8 ) |
                        ( ( uint )( pixelColour.G >> 4 ) <<  4 ) |
                        ( ( uint )( pixelColour.B >> 4 ) <<  0 );
                  break;
                }

                case  DdsFileFormat.DDS_FORMAT_R8G8B8:
                {
                  pixelData = ( ( uint )pixelColour.R << 16 ) |
                        ( ( uint )pixelColour.G <<  8 ) |
                        ( ( uint )pixelColour.B <<  0 );
                  break;
                }

                case  DdsFileFormat.DDS_FORMAT_R5G6B5:
                {
                  pixelData = ( ( uint )( pixelColour.R >> 3 ) << 11 ) |
                        ( ( uint )( pixelColour.G >> 2 ) <<  5 ) |
                        ( ( uint )( pixelColour.B >> 3 ) <<  0 );
                  break;
                }
              }

              // pixelData contains our target data.. so now set the pixel bytes
              int  pixelOffset  = ( y * mipPitch ) + ( x * pixelWidth );
              for ( int loop = 0; loop < pixelWidth; loop++ )
              {
                outputData[ pixelOffset + loop ] = ( byte )( ( pixelData >> ( 8 * loop ) ) & 0xff );
              }
            }

                        if (progressCallback != null)
                        {
                            long thisMipPixelsDone = (y + 1) * (long)mipWidth;
                            long previousMipsPixelsDone = pixelsCompleted[mipLoop];
                            double progress = (double)((double)thisMipPixelsDone + (double)previousMipsPixelsDone) / (double)totalPixels;
                            progressCallback(this, new ProgressEventArgs(100.0 * progress));
                        }
          }
        }

        // Write the data for this mip level out.. 
        output.Write( outputData, 0, outputData.GetLength( 0 ) );

        mipWidth = mipWidth / 2;
        mipHeight = mipHeight / 2;
      }
    }

    public  void  Load( System.IO.Stream input )
    {
      // Read the DDS tag. If it's not right, then bail.. 
      uint  ddsTag = ( uint )Utility.ReadUInt32( input );
      if ( ddsTag != 0x20534444 )
        throw new FormatException( "File does not appear to be a DDS image" );

      // Read everything in.. for now assume it worked like a charm..
      m_header.Read( input );

      if ( ( m_header.m_pixelFormat.m_flags & ( int )DdsPixelFormat.PixelFormatFlags.DDS_FOURCC ) != 0 )
      {
        int  squishFlags = 0;

        switch ( m_header.m_pixelFormat.m_fourCC )
        {
          case  0x31545844:
            squishFlags = ( int )DdsSquish.SquishFlags.kDxt1;
            break;

          case  0x33545844:
            squishFlags = ( int )DdsSquish.SquishFlags.kDxt3;
            break;

          case  0x35545844:
            squishFlags = ( int )DdsSquish.SquishFlags.kDxt5;
            break;

          default:
            throw new FormatException( "File is not a supported DDS format" );
        }

        // Compute size of compressed block area
        int blockCount = ( ( GetWidth() + 3 )/4 ) * ( ( GetHeight() + 3 )/4 );
        int blockSize = ( ( squishFlags & ( int )DdsSquish.SquishFlags.kDxt1 ) != 0 ) ? 8 : 16;
        
        // Allocate room for compressed blocks, and read data into it.
        byte[] compressedBlocks = new byte[ blockCount * blockSize ];
        input.Read( compressedBlocks, 0, compressedBlocks.GetLength( 0 ) );

        // Now decompress..
        m_pixelData = DdsSquish.DecompressImage( compressedBlocks, GetWidth(), GetHeight(), squishFlags );
      }
      else
      {
        // We can only deal with the non-DXT formats we know about..  this is a bit of a mess..
        // Sorry..
        DdsFileFormat  fileFormat = DdsFileFormat.DDS_FORMAT_INVALID;

        if (  ( m_header.m_pixelFormat.m_flags == ( int )DdsPixelFormat.PixelFormatFlags.DDS_RGBA ) && 
            ( m_header.m_pixelFormat.m_rgbBitCount == 32 ) && 
            ( m_header.m_pixelFormat.m_rBitMask == 0x00ff0000 ) && ( m_header.m_pixelFormat.m_gBitMask == 0x0000ff00 ) &&
            ( m_header.m_pixelFormat.m_bBitMask == 0x000000ff ) && ( m_header.m_pixelFormat.m_aBitMask == 0xff000000 ) )
          fileFormat = DdsFileFormat.DDS_FORMAT_A8R8G8B8;
        else
        if (  ( m_header.m_pixelFormat.m_flags == ( int )DdsPixelFormat.PixelFormatFlags.DDS_RGB ) && 
            ( m_header.m_pixelFormat.m_rgbBitCount == 32 ) && 
            ( m_header.m_pixelFormat.m_rBitMask == 0x00ff0000 ) && ( m_header.m_pixelFormat.m_gBitMask == 0x0000ff00 ) &&
            ( m_header.m_pixelFormat.m_bBitMask == 0x000000ff ) && ( m_header.m_pixelFormat.m_aBitMask == 0x00000000 ) )
          fileFormat = DdsFileFormat.DDS_FORMAT_X8R8G8B8;
        else
        if (  ( m_header.m_pixelFormat.m_flags == ( int )DdsPixelFormat.PixelFormatFlags.DDS_RGBA ) && 
            ( m_header.m_pixelFormat.m_rgbBitCount == 32 ) && 
            ( m_header.m_pixelFormat.m_rBitMask == 0x000000ff ) && ( m_header.m_pixelFormat.m_gBitMask == 0x0000ff00 ) &&
            ( m_header.m_pixelFormat.m_bBitMask == 0x00ff0000 ) && ( m_header.m_pixelFormat.m_aBitMask == 0xff000000 ) )
          fileFormat = DdsFileFormat.DDS_FORMAT_A8B8G8R8;
        else
        if (  ( m_header.m_pixelFormat.m_flags == ( int )DdsPixelFormat.PixelFormatFlags.DDS_RGB ) && 
            ( m_header.m_pixelFormat.m_rgbBitCount == 32 ) && 
            ( m_header.m_pixelFormat.m_rBitMask == 0x000000ff ) && ( m_header.m_pixelFormat.m_gBitMask == 0x0000ff00 ) &&
            ( m_header.m_pixelFormat.m_bBitMask == 0x00ff0000 ) && ( m_header.m_pixelFormat.m_aBitMask == 0x00000000 ) )
          fileFormat = DdsFileFormat.DDS_FORMAT_X8B8G8R8;
        else
        if (  ( m_header.m_pixelFormat.m_flags == ( int )DdsPixelFormat.PixelFormatFlags.DDS_RGBA ) && 
            ( m_header.m_pixelFormat.m_rgbBitCount == 16 ) && 
            ( m_header.m_pixelFormat.m_rBitMask == 0x00007c00 ) && ( m_header.m_pixelFormat.m_gBitMask == 0x000003e0 ) &&
            ( m_header.m_pixelFormat.m_bBitMask == 0x0000001f ) && ( m_header.m_pixelFormat.m_aBitMask == 0x00008000 ) )
          fileFormat = DdsFileFormat.DDS_FORMAT_A1R5G5B5;
        else
        if (  ( m_header.m_pixelFormat.m_flags == ( int )DdsPixelFormat.PixelFormatFlags.DDS_RGBA ) && 
            ( m_header.m_pixelFormat.m_rgbBitCount == 16 ) && 
            ( m_header.m_pixelFormat.m_rBitMask == 0x00000f00 ) && ( m_header.m_pixelFormat.m_gBitMask == 0x000000f0 ) &&
            ( m_header.m_pixelFormat.m_bBitMask == 0x0000000f ) && ( m_header.m_pixelFormat.m_aBitMask == 0x0000f000 ) )
          fileFormat = DdsFileFormat.DDS_FORMAT_A4R4G4B4;
        else
        if (  ( m_header.m_pixelFormat.m_flags == ( int )DdsPixelFormat.PixelFormatFlags.DDS_RGB ) && 
            ( m_header.m_pixelFormat.m_rgbBitCount == 24 ) && 
            ( m_header.m_pixelFormat.m_rBitMask == 0x00ff0000 ) && ( m_header.m_pixelFormat.m_gBitMask == 0x0000ff00 ) &&
            ( m_header.m_pixelFormat.m_bBitMask == 0x000000ff ) && ( m_header.m_pixelFormat.m_aBitMask == 0x00000000 ) )
          fileFormat = DdsFileFormat.DDS_FORMAT_R8G8B8;
        else
        if (  ( m_header.m_pixelFormat.m_flags == ( int )DdsPixelFormat.PixelFormatFlags.DDS_RGB ) && 
            ( m_header.m_pixelFormat.m_rgbBitCount == 16 ) && 
            ( m_header.m_pixelFormat.m_rBitMask == 0x0000f800 ) && ( m_header.m_pixelFormat.m_gBitMask == 0x000007e0 ) &&
            ( m_header.m_pixelFormat.m_bBitMask == 0x0000001f ) && ( m_header.m_pixelFormat.m_aBitMask == 0x00000000 ) )
          fileFormat = DdsFileFormat.DDS_FORMAT_R5G6B5;

        // If fileFormat is still invalid, then it's an unsupported format.
        if ( fileFormat == DdsFileFormat.DDS_FORMAT_INVALID )
          throw new FormatException( "File is not a supported DDS format" );  

        // Size of a source pixel, in bytes
        int srcPixelSize = ( ( int )m_header.m_pixelFormat.m_rgbBitCount / 8 );

        // We need the pitch for a row, so we can allocate enough memory for the load.
        int rowPitch = 0;

        if ( ( m_header.m_headerFlags & ( int )DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_PITCH ) != 0 )  
        {
          // Pitch specified.. so we can use directly
          rowPitch = ( int )m_header.m_pitchOrLinearSize;
        }
        else
        if ( ( m_header.m_headerFlags & ( int )DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_LINEARSIZE ) != 0 )
        {
          // Linear size specified.. compute row pitch. Of course, this should never happen
          // as linear size is *supposed* to be for compressed textures. But Microsoft don't 
          // always play by the rules when it comes to DDS output. 
          rowPitch = ( int )m_header.m_pitchOrLinearSize / ( int )m_header.m_height;
        }
        else
        {
          // Another case of Microsoft not obeying their standard is the 'Convert to..' shell extension
          // that ships in the DirectX SDK. Seems to always leave flags empty..so no indication of pitch
          // or linear size. And - to cap it all off - they leave pitchOrLinearSize as *zero*. Zero??? If
          // we get this bizarre set of inputs, we just go 'screw it' and compute row pitch ourselves, 
          // making sure we DWORD align it (if that code path is enabled).
          rowPitch = ( ( int )m_header.m_width * srcPixelSize );

#if  APPLY_PITCH_ALIGNMENT
          rowPitch = ( ( ( int )rowPitch + 3 ) & ( ~3 ) );
#endif  // APPLY_PITCH_ALIGNMENT
        }

//        System.Diagnostics.Debug.WriteLine( "Image width : " + m_header.m_width + ", rowPitch = " + rowPitch );

        // Ok.. now, we need to allocate room for the bytes to read in from.. it's rowPitch bytes * height
        byte[] readPixelData = new byte[ rowPitch * m_header.m_height ];
        input.Read( readPixelData, 0, readPixelData.GetLength( 0 ) );

        // We now need space for the real pixel data.. that's width * height * 4..
        m_pixelData = new byte[ m_header.m_width * m_header.m_height * 4 ];

        // And now we have the arduous task of filling that up with stuff..
        for ( int destY = 0; destY < ( int )m_header.m_height; destY++ )  
        {
          for ( int destX = 0; destX < ( int )m_header.m_width; destX++ )  
          {
            // Compute source pixel offset
            int  srcPixelOffset = ( destY * rowPitch ) + ( destX * srcPixelSize );

            // Read our pixel
            uint  pixelColour = 0;
            uint  pixelRed  = 0;
            uint  pixelGreen  = 0;  
            uint  pixelBlue  = 0;
            uint  pixelAlpha  = 0;

            // Build our pixel colour as a DWORD  
            for ( int loop = 0; loop < srcPixelSize; loop++ )
            {
              pixelColour |= ( uint )( readPixelData[ srcPixelOffset + loop ] << ( 8 * loop ) );
            }

            if ( fileFormat == DdsFileFormat.DDS_FORMAT_A8R8G8B8 )
            {
              pixelAlpha  = ( pixelColour >> 24 ) & 0xff;
              pixelRed  = ( pixelColour >> 16 ) & 0xff;
              pixelGreen  = ( pixelColour >> 8  ) & 0xff;
              pixelBlue  = ( pixelColour >> 0  ) & 0xff;
            }
            else
            if ( fileFormat == DdsFileFormat.DDS_FORMAT_X8R8G8B8 )
            {
              pixelAlpha  = 0xff;
              pixelRed  = ( pixelColour >> 16 ) & 0xff;
              pixelGreen  = ( pixelColour >> 8  ) & 0xff;
              pixelBlue  = ( pixelColour >> 0  ) & 0xff;
            }
            else
            if ( fileFormat == DdsFileFormat.DDS_FORMAT_A8B8G8R8 )
            {
              pixelAlpha  = ( pixelColour >> 24 ) & 0xff;
              pixelRed  = ( pixelColour >> 0  ) & 0xff;
              pixelGreen  = ( pixelColour >> 8  ) & 0xff;
              pixelBlue  = ( pixelColour >> 16 ) & 0xff;
            }
            else
            if ( fileFormat == DdsFileFormat.DDS_FORMAT_X8B8G8R8 )
            {
              pixelAlpha  = 0xff;
              pixelRed  = ( pixelColour >> 0  ) & 0xff;
              pixelGreen  = ( pixelColour >> 8  ) & 0xff;
              pixelBlue  = ( pixelColour >> 16 ) & 0xff;
            }
            else
            if ( fileFormat == DdsFileFormat.DDS_FORMAT_A1R5G5B5 )
            {
              pixelAlpha  = ( pixelColour >> 15 ) * 0xff;
              pixelRed  = ( pixelColour >> 10 ) & 0x1f;
              pixelGreen  = ( pixelColour >> 5  ) & 0x1f;
              pixelBlue  = ( pixelColour >> 0  ) & 0x1f;

              pixelRed  = ( pixelRed   << 3 ) | ( pixelRed   >> 2 );
              pixelGreen  = ( pixelGreen << 3 ) | ( pixelGreen >> 2 );
              pixelBlue  = ( pixelBlue  << 3 ) | ( pixelBlue  >> 2 );
            }
            else
            if ( fileFormat == DdsFileFormat.DDS_FORMAT_A4R4G4B4 )
            {
              pixelAlpha  = ( pixelColour >> 12 ) & 0xff;
              pixelRed  = ( pixelColour >> 8  ) & 0x0f;
              pixelGreen  = ( pixelColour >> 4  ) & 0x0f;
              pixelBlue  = ( pixelColour >> 0  ) & 0x0f;

              pixelAlpha  = ( pixelAlpha << 4 ) | ( pixelAlpha >> 0 );
              pixelRed  = ( pixelRed   << 4 ) | ( pixelRed   >> 0 );
              pixelGreen  = ( pixelGreen << 4 ) | ( pixelGreen >> 0 );
              pixelBlue  = ( pixelBlue  << 4 ) | ( pixelBlue  >> 0 );
            }
            else
            if ( fileFormat == DdsFileFormat.DDS_FORMAT_R8G8B8 )
            {
              pixelAlpha  = 0xff;
              pixelRed  = ( pixelColour >> 16 ) & 0xff;
              pixelGreen  = ( pixelColour >> 8  ) & 0xff;
              pixelBlue  = ( pixelColour >> 0  ) & 0xff;
            }
            else
            if ( fileFormat == DdsFileFormat.DDS_FORMAT_R5G6B5 )
            {
              pixelAlpha  = 0xff;
              pixelRed  = ( pixelColour >> 11 ) & 0x1f;
              pixelGreen  = ( pixelColour >> 5  ) & 0x3f;
              pixelBlue  = ( pixelColour >> 0  ) & 0x1f;

              pixelRed  = ( pixelRed   << 3 ) | ( pixelRed   >> 2 );
              pixelGreen  = ( pixelGreen << 2 ) | ( pixelGreen >> 4 );
              pixelBlue  = ( pixelBlue  << 3 ) | ( pixelBlue  >> 2 );
            }
                              
            // Write the colours away..
            int  destPixelOffset  = ( destY * ( int )m_header.m_width * 4 ) + ( destX * 4 );
            m_pixelData[ destPixelOffset + 0 ]  = ( byte )pixelRed;
            m_pixelData[ destPixelOffset + 1 ]  = ( byte )pixelGreen;
            m_pixelData[ destPixelOffset + 2 ]  = ( byte )pixelBlue;
            m_pixelData[ destPixelOffset + 3 ]  = ( byte )pixelAlpha;
          }
        }  
      }
    }
  
    public  int    GetWidth()
    {
      return ( int )m_header.m_width;
    }

    public  int    GetHeight()
    {
      return ( int )m_header.m_height;
    }

    public  byte[]  GetPixelData()
    {
      return m_pixelData;
    }

    // Loaded DDS header (also uses storage for save)
    public  DdsHeader  m_header;
  
    // Pixel data
    byte[]        m_pixelData;
    
  }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.