Example #1
0
 public void Load(Stream s)
 {
     Header                     = new DDS_Header();
     Header.magic               = ReadUInt(s);
     Header.dwSize              = ReadUInt(s);
     Header.dwFlags             = ReadUInt(s);
     Header.dwHeight            = ReadUInt(s);
     Header.dwWidth             = ReadUInt(s);
     Header.dwPitchOrLinearSize = ReadUInt(s);
     Header.dwDepth             = ReadUInt(s);
     Header.dwMipMapCount       = ReadUInt(s);
     Header.dwReserved1         = new uint[11];
     for (int i = 0; i < 11; i++)
     {
         Header.dwReserved1[i] = ReadUInt(s);
     }
     Header.ddspf       = ReadPixelFormat(s);
     Header.dwCaps      = ReadUInt(s);
     Header.dwCaps2     = ReadUInt(s);
     Header.dwCaps3     = ReadUInt(s);
     Header.dwCaps4     = ReadUInt(s);
     Header.dwReserved2 = ReadUInt(s);
     if (Header.ddspf.dwFourCC == 0x30315844)
     {
         Header.extension                   = new DDS_HEADER_DXT10();
         Header.extension.dxgiFormat        = (DXGI_FORMAT)ReadUInt(s);
         Header.extension.resourceDimension = (D3D10_RESOURCE_DIMENSION)ReadUInt(s);
         Header.extension.miscFlag          = ReadUInt(s);
         Header.extension.arraySize         = ReadUInt(s);
         Header.extension.miscFlags2        = (ALPHA_MODE)ReadUInt(s);
     }
 }
Example #2
0
        public static TextureInfo GetTextureInfo(Texture texture)
        {
            // Load DDS header so we can extract some info from it
            var ddsHeader = new DDS_Header(new MemoryStream(texture.Data));

            TexturePixelFormat format;

            switch (ddsHeader.Format)
            {
            case ImageEngineFormat.DDS_DXT1:
                format = TexturePixelFormat.DXT1;
                break;

            case ImageEngineFormat.DDS_DXT3:
                format = TexturePixelFormat.DXT3;
                break;

            case ImageEngineFormat.DDS_DXT5:
                format = TexturePixelFormat.DXT5;
                break;

            case ImageEngineFormat.DDS_ARGB_8:
                format = TexturePixelFormat.ARGB;
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            return(new TextureInfo(texture, ddsHeader.Width, ddsHeader.Height, ddsHeader.dwMipMapCount, format));
        }
Example #3
0
        public static byte[] DecodeToDDS(FieldTexturePS3 texture)
        {
            var surfaceFormat = ImageEngineFormat.DDS_DXT1;

            if (texture.Flags.HasFlag(FieldTextureFlags.DXT3))
            {
                surfaceFormat = ImageEngineFormat.DDS_DXT3;
            }
            else if (texture.Flags.HasFlag(FieldTextureFlags.DXT5))
            {
                surfaceFormat = ImageEngineFormat.DDS_DXT5;
            }

            var ddsBytes = new byte[0x80 + texture.DataLength];

            // create & write header
            var ddsHeader = new DDS_Header(texture.MipMapCount, texture.Height, texture.Width, surfaceFormat);

            ddsHeader.WriteToArray(ddsBytes, 0);

            // write pixel data
            Array.Copy(texture.Data, 0, ddsBytes, 0x80, texture.DataLength);

            return(ddsBytes);
        }
Example #4
0
        /// <summary>
        /// Determines image type via headers.
        /// Keeps stream position.
        /// </summary>
        /// <param name="imgData">Image data, incl header.</param>
        /// <returns>Type of image.</returns>
        public static SupportedExtensions DetermineImageType(Stream imgData)
        {
            SupportedExtensions ext = SupportedExtensions.UNKNOWN;

            // KFreon: Save position and go back to start
            long originalPos = imgData.Position;

            imgData.Seek(0, SeekOrigin.Begin);

            var bits = new byte[8];

            imgData.Read(bits, 0, 8);

            // BMP
            if (BMP_Header.CheckIdentifier(bits))
            {
                ext = SupportedExtensions.BMP;
            }

            // PNG
            if (PNG_Header.CheckIdentifier(bits))
            {
                ext = SupportedExtensions.PNG;
            }

            // JPG
            if (JPG_Header.CheckIdentifier(bits))
            {
                ext = SupportedExtensions.JPG;
            }

            // DDS
            if (DDS_Header.CheckIdentifier(bits))
            {
                ext = SupportedExtensions.DDS;
            }

            // GIF
            if (GIF_Header.CheckIdentifier(bits))
            {
                ext = SupportedExtensions.GIF;
            }

            if (TIFF_Header.CheckIdentifier(bits))
            {
                ext = SupportedExtensions.TIF;
            }

            // TGA (assumed if no other matches
            if (ext == SupportedExtensions.UNKNOWN)
            {
                ext = SupportedExtensions.TGA;
            }

            // KFreon: Reset stream position
            imgData.Seek(originalPos, SeekOrigin.Begin);

            return(ext);
        }
Example #5
0
        protected override void _Read(BinaryReader reader)
        {
            long baseOffset = reader.BaseStream.Position;

            byte[] buffer = reader.ReadBytes((int)reader.BaseStream.Length);

            MemoryStream memoryStream = new MemoryStream(buffer, 0, buffer.Length, true, true);
            DDS_Header   header       = new DDS_Header(memoryStream);

            FormatDetails = new DDSFormatDetails(header.Format, header.DX10_DXGI_AdditionalHeader.dxgiFormat);
            MipMaps       = DDSGeneral.LoadDDS(memoryStream, header, 0, FormatDetails);
            Width         = header.Width;
            Height        = header.Height;
            memoryStream.Close();
        }
        internal static AbstractHeader LoadHeader(Stream stream)
        {
            stream.Seek(0, SeekOrigin.Begin);

            // Determine type of image
            ImageFormats.SupportedExtensions ext = ImageFormats.DetermineImageType(stream);

            // Parse header
            AbstractHeader header = null;

            switch (ext)
            {
            case ImageFormats.SupportedExtensions.BMP:
                header = new BMP_Header(stream);
                break;

            case ImageFormats.SupportedExtensions.DDS:
                header = new DDS_Header(stream);
                break;

            case ImageFormats.SupportedExtensions.JPG:
                header = new JPG_Header(stream);
                break;

            case ImageFormats.SupportedExtensions.PNG:
                header = new PNG_Header(stream);
                break;

            case ImageFormats.SupportedExtensions.TGA:
                header = new TGA_Header(stream);
                break;

            case ImageFormats.SupportedExtensions.GIF:
                header = new GIF_Header(stream);
                break;

            case ImageFormats.SupportedExtensions.TIF:
                header = new TIFF_Header(stream);
                break;

            default:
                throw new NotSupportedException("Image type unknown.");
            }
            return(header);
        }
        public static byte[] DecodeToDDS(GNFTexture texture)
        {
            var surfaceFormat = ImageEngineFormat.DDS_DXT1;

            if (texture.PixelFormat == 0x50)
            {
                surfaceFormat = ImageEngineFormat.DDS_DXT3;
            }

            var ddsBytes = new byte[0x80 + texture.Data.Length];

            // create & write header
            var ddsHeader = new DDS_Header(1, texture.Height, texture.Width, surfaceFormat);

            ddsHeader.WriteToArray(ddsBytes, 0);

            // write pixel data
            Array.Copy(texture.Data, 0, ddsBytes, 0x80, texture.Data.Length);

            return(ddsBytes);
        }
Example #8
0
        internal static byte[] Save(List <MipMap> mipMaps, ImageFormats.ImageEngineFormatDetails destFormatDetails, AlphaSettings alphaSetting)
        {
            // Set compressor for Block Compressed textures
            Action <byte[], int, int, byte[], int, AlphaSettings, ImageFormats.ImageEngineFormatDetails> compressor = destFormatDetails.BlockEncoder;

            bool needCheckSize = destFormatDetails.IsBlockCompressed;

            int height = mipMaps[0].Height;
            int width  = mipMaps[0].Width;

            if (needCheckSize && !CheckSize_DXT(width, height))
            {
                throw new InvalidOperationException($"DXT compression formats require dimensions to be multiples of 4. Got: {width}x{height}.");
            }

            // Create header and write to destination
            DDS_Header header = new DDS_Header(mipMaps.Count, height, width, destFormatDetails.Format, destFormatDetails.DX10Format);

            int headerLength = destFormatDetails.HeaderSize;

            int fullSize = GetCompressedSizeOfImage(mipMaps.Count, destFormatDetails, width, height);

            /*if (destFormatDetails.ComponentSize != 1)
             *  fullSize += (fullSize - headerLength) * destFormatDetails.ComponentSize;*/// Size adjustment for destination to allow for different component sizes.

            byte[] destination = new byte[fullSize];
            header.WriteToArray(destination, 0);

            int blockSize = destFormatDetails.BlockSize;

            if (destFormatDetails.IsBlockCompressed)
            {
                int mipOffset = headerLength;
                foreach (MipMap mipmap in mipMaps)
                {
                    if (ImageEngine.IsCancellationRequested)
                    {
                        break;
                    }

                    var temp = WriteCompressedMipMap(destination, mipOffset, mipmap, blockSize, compressor, alphaSetting);
                    if (temp != -1)  // When dimensions too low.
                    {
                        mipOffset = temp;
                    }
                }
            }
            else
            {
                // UNCOMPRESSED
                var action = new Action <int>(mipIndex =>
                {
                    if (alphaSetting == AlphaSettings.RemoveAlphaChannel)
                    {
                        // Remove alpha by setting AMask = 0
                        var ddspf        = header.ddspf;
                        ddspf.dwABitMask = 0;
                        header.ddspf     = ddspf;
                    }

                    // Get MipOffset
                    int offset = GetMipOffset(mipIndex, destFormatDetails, width, height);

                    WriteUncompressedMipMap(destination, offset, mipMaps[mipIndex], destFormatDetails, header.ddspf);
                });

                if (ImageEngine.EnableThreading)
                {
                    Parallel.For(0, mipMaps.Count, new ParallelOptions {
                        MaxDegreeOfParallelism = ImageEngine.NumThreads
                    }, (mip, loopState) =>
                    {
                        if (ImageEngine.IsCancellationRequested)
                        {
                            loopState.Stop();
                        }

                        action(mip);
                    });
                }
                else
                {
                    for (int i = 0; i < mipMaps.Count; i++)
                    {
                        if (ImageEngine.IsCancellationRequested)
                        {
                            break;
                        }

                        action(i);
                    }
                }
            }

            return(ImageEngine.IsCancellationRequested ? null : destination);
        }
Example #9
0
        internal static List <MipMap> LoadDDS(MemoryStream compressed, DDS_Header header, int desiredMaxDimension, ImageFormats.ImageEngineFormatDetails formatDetails)
        {
            MipMap[] MipMaps = null;

            int mipWidth             = header.Width;
            int mipHeight            = header.Height;
            ImageEngineFormat format = header.Format;

            int estimatedMips  = header.dwMipMapCount;
            int mipOffset      = formatDetails.HeaderSize;
            int originalOffset = mipOffset;

            if (!EnsureMipInImage(compressed.Length, mipWidth, mipHeight, 4, formatDetails, out mipOffset))  // Update number of mips too
            {
                estimatedMips = 1;
            }

            if (estimatedMips == 0)
            {
                estimatedMips = EstimateNumMipMaps(mipWidth, mipHeight);
            }

            mipOffset = originalOffset;  // Needs resetting after checking there's mips in this image.

            // Ensure there's at least 1 mipmap
            if (estimatedMips == 0)
            {
                estimatedMips = 1;
            }

            int orig_estimatedMips = estimatedMips; // Store original count for later (uncompressed only I think)

            // KFreon: Decide which mip to start loading at - going to just load a few mipmaps if asked instead of loading all, then choosing later. That's slow.
            if (desiredMaxDimension != 0 && estimatedMips > 1)
            {
                if (!EnsureMipInImage(compressed.Length, mipWidth, mipHeight, desiredMaxDimension, formatDetails, out mipOffset))  // Update number of mips too
                {
                    throw new InvalidDataException($"Requested mipmap does not exist in this image. Top Image Size: {mipWidth}x{mipHeight}, requested mip max dimension: {desiredMaxDimension}.");
                }

                // Not the first mipmap.
                if (mipOffset > formatDetails.HeaderSize)
                {
                    double divisor = mipHeight > mipWidth ? mipHeight / desiredMaxDimension : mipWidth / desiredMaxDimension;
                    mipHeight = (int)(mipHeight / divisor);
                    mipWidth  = (int)(mipWidth / divisor);

                    if (mipWidth == 0 || mipHeight == 0)  // Reset as a dimension is too small to resize
                    {
                        mipHeight = header.Height;
                        mipWidth  = header.Width;
                        mipOffset = formatDetails.HeaderSize;
                    }
                    else
                    {
                        // Update estimated mips due to changing dimensions.
                        estimatedMips = EstimateNumMipMaps(mipWidth, mipHeight);
                    }
                }
                else  // The first mipmap
                {
                    mipOffset = formatDetails.HeaderSize;
                }
            }

            // Move to requested mipmap
            compressed.Position = mipOffset;

            // Block Compressed texture chooser.
            Action <byte[], int, byte[], int, int, bool> DecompressBCBlock = formatDetails.BlockDecoder;

            MipMaps = new MipMap[estimatedMips];
            int blockSize = formatDetails.BlockSize;

            // KFreon: Read mipmaps
            if (formatDetails.IsBlockCompressed)    // Threading done in the decompression, not here.
            {
                for (int m = 0; m < estimatedMips; m++)
                {
                    if (ImageEngine.IsCancellationRequested)
                    {
                        break;
                    }


                    // KFreon: If mip is too small, skip out. This happens most often with non-square textures. I think it's because the last mipmap is square instead of the same aspect.
                    // Don't do the mip size check here (<4) since we still need to have a MipMap object for those lower than this for an accurate count.
                    if (mipWidth <= 0 || mipHeight <= 0)  // Needed cos it doesn't throw when reading past the end for some reason.
                    {
                        break;
                    }

                    MipMap mipmap = ReadCompressedMipMap(compressed, mipWidth, mipHeight, mipOffset, formatDetails, DecompressBCBlock);
                    MipMaps[m] = mipmap;
                    mipOffset += (int)(mipWidth * mipHeight * (blockSize / 16d)); // Also put the division in brackets cos if the mip dimensions are high enough, the multiplications can exceed int.MaxValue)
                    mipWidth  /= 2;
                    mipHeight /= 2;
                }
            }
            else
            {
                int startMip = orig_estimatedMips - estimatedMips;

                // UNCOMPRESSED - Can't really do threading in "decompression" so do it for the mipmaps.
                var action = new Action <int>(mipIndex =>
                {
                    // Calculate mipOffset and dimensions
                    int offset, width, height;
                    offset = GetMipOffset(mipIndex, formatDetails, header.Width, header.Height);

                    double divisor = mipIndex == 0 ? 1d : 2 << (mipIndex - 1);   // Divisor represents 2^mipIndex - Math.Pow seems very slow.
                    width          = (int)(header.Width / divisor);
                    height         = (int)(header.Height / divisor);

                    MipMap mipmap = null;
                    try
                    {
                        mipmap = ReadUncompressedMipMap(compressed, offset, width, height, header.ddspf, formatDetails);
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine(e.ToString());
                    }

                    MipMaps[mipIndex] = mipmap;
                });

                if (ImageEngine.EnableThreading)
                {
                    Parallel.For(startMip, orig_estimatedMips, new ParallelOptions {
                        MaxDegreeOfParallelism = ImageEngine.NumThreads
                    }, (mip, loopState) =>
                    {
                        if (ImageEngine.IsCancellationRequested)
                        {
                            loopState.Stop();
                        }

                        action(mip);
                    });
                }
                else
                {
                    for (int i = startMip; i < orig_estimatedMips; i++)
                    {
                        if (ImageEngine.IsCancellationRequested)
                        {
                            break;
                        }

                        action(i);
                    }
                }
            }

            List <MipMap> mips = new List <MipMap>(MipMaps.Where(t => t != null));

            if (mips.Count == 0)
            {
                throw new InvalidOperationException($"No mipmaps loaded. Estimated mips: {estimatedMips}, mip dimensions: {mipWidth}x{mipHeight}");
            }
            return(mips);
        }
Example #10
0
        public static SBSurface Import(string FileName)
        {
            SBSurface surface = new SBSurface();

            using (BinaryReaderExt reader = new BinaryReaderExt(new FileStream(FileName, FileMode.Open)))
            {
                DDS_Header header = new DDS_Header();
                header.Read(reader);

                surface.Name   = Path.GetFileNameWithoutExtension(FileName);
                surface.Width  = header.dwWidth;
                surface.Height = header.dwHeight;
                if (header.dwFlags.HasFlag(DDSD.DEPTH))
                {
                    surface.Depth = header.dwDepth;
                }
                else
                {
                    surface.Depth = 1;
                }

                if (header.ddspf.dwFourCC == 0x31545844)
                {
                    surface.InternalFormat = InternalFormat.CompressedRgbaS3tcDxt1Ext;
                }
                else
                if (header.ddspf.dwFourCC == 0x30315844)
                {
                    surface.InternalFormat = DXGItoInternal(header.DXT10Header.dxgiFormat);
                    if (surface.InternalFormat == 0)
                    {
                        System.Windows.Forms.MessageBox.Show("DDS format not supported " + header.DXT10Header.dxgiFormat);

                        return(null);
                    }
                }
                else
                {
                    if (((FourCC_DXGI)header.ddspf.dwFourCC) == FourCC_DXGI.D3DFMT_A32B32G32R32F)
                    {
                        surface.InternalFormat = InternalFormat.Rgba32f;
                        surface.PixelFormat    = PixelFormat.Rgba;
                        surface.PixelType      = PixelType.Float;
                    }
                    else
                    {
                        System.Windows.Forms.MessageBox.Show("DDS format not supported " + header.ddspf.dwFourCC.ToString("X"));
                        return(null);
                    }
                }


                // TODO: read other mips
                int w = surface.Width;
                int h = surface.Height;
                //SBConsole.WriteLine(header.dwCaps.ToString() + " " + header.dwCaps2.ToString() + " " + header.dwFlags.ToString());
                for (int array = 0; array < (header.dwCaps2.HasFlag(DDSCAPS2.CUBEMAP_ALLFACES) ? 6 : 1); array++)
                {
                    w = surface.Width;
                    h = surface.Height;
                    var mip = new MipArray();

                    for (int i = 0; i < (header.dwFlags.HasFlag(DDSD.MIPMAPCOUNT) ? header.dwMipMapCount : 1); i++)
                    {
                        var mipSize = Math.Max(1, ((w + 3) / 4)) * Math.Max(1, ((h + 3) / 4)) * (int)TextureFormatInfo.GetBPP(surface.InternalFormat);

                        if (mipSize % TextureFormatInfo.GetBPP(surface.InternalFormat) != 0)
                        {
                            mipSize += (int)(TextureFormatInfo.GetBPP(surface.InternalFormat) - (mipSize % TextureFormatInfo.GetBPP(surface.InternalFormat)));
                        }

                        var data = reader.ReadBytes(mipSize);

                        mip.Mipmaps.Add(data);
                        w /= 2;
                        h /= 2;
                    }

                    surface.Arrays.Add(mip);
                }
                if (reader.Position != reader.BaseStream.Length)
                {
                    SBConsole.WriteLine("Warning: error reading dds " + reader.Position.ToString("X"));
                }
            }



            return(surface);
        }
Example #11
0
        public static void Export(string fileName, SBSurface surface)
        {
            if (!internalFormatToDXGI.ContainsKey(surface.InternalFormat) && !internalFormatToD3D.ContainsKey(surface.InternalFormat))
            {
                System.Windows.Forms.MessageBox.Show("Unsupported DDS export format " + surface.InternalFormat.ToString());
                return;
            }
            var Header = new DDS_Header()
            {
                dwFlags             = (DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PIXELFORMAT | DDSD.LINEARSIZE),
                dwHeight            = surface.Height,
                dwWidth             = surface.Width,
                dwPitchOrLinearSize = GetPitchOrLinearSize(surface.InternalFormat, surface.Width),
                dwDepth             = surface.Depth,
                dwMipMapCount       = surface.Arrays[0].Mipmaps.Count,
                dwReserved1         = new uint[11],
                ddspf = new DDS_PIXELFORMAT()
                {
                },
                dwCaps  = 0,
                dwCaps2 = 0
            };

            if (surface.Arrays.Count > 0 && surface.Arrays[0].Mipmaps.Count > 1)
            {
                Header.dwFlags |= DDSD.MIPMAPCOUNT;
            }

            if (surface.IsCubeMap)
            {
                Header.dwCaps  |= DDSCAPS.TEXTURE | DDSCAPS.COMPLEX;
                Header.dwCaps2 |= DDSCAPS2.CUBEMAP_ALLFACES;
            }
            //TODO: format
            Header.ddspf.dwFlags  = DDPF.FOURCC;
            Header.ddspf.dwFourCC = 0x30315844;
            if (internalFormatToD3D.ContainsKey(surface.InternalFormat))
            {
                Header.ddspf.dwFourCC = (int)internalFormatToD3D[surface.InternalFormat];
            }

            if (internalFormatToDXGI.ContainsKey(surface.InternalFormat))
            {
                Header.DXT10Header.dxgiFormat        = internalFormatToDXGI[surface.InternalFormat];
                Header.DXT10Header.resourceDimension = D3D10_RESOURCE_DIMENSION.D3D10_RESOURCE_DIMENSION_TEXTURE2D;
                Header.DXT10Header.arraySize         = (uint)surface.Arrays.Count;
            }

            using (BinaryWriterExt writer = new BinaryWriterExt(new FileStream(fileName, FileMode.Create)))
            {
                Header.Write(writer);

                foreach (var arr in surface.Arrays)
                {
                    foreach (var mip in arr.Mipmaps)
                    {
                        writer.Write(mip);
                    }
                }
            }
        }
Example #12
0
 public void Load(Stream s)
 {
     Header = new DDS_Header();
     Header.magic = ReadUInt(s);
     Header.dwSize = ReadUInt(s);
     Header.dwFlags = ReadUInt(s);
     Header.dwHeight = ReadUInt(s);
     Header.dwWidth = ReadUInt(s);
     Header.dwPitchOrLinearSize = ReadUInt(s);
     Header.dwDepth = ReadUInt(s);
     Header.dwMipMapCount = ReadUInt(s);
     Header.dwReserved1 = new uint[11];
     for (int i = 0; i < 11; i++)
         Header.dwReserved1[i] = ReadUInt(s);
     Header.ddspf = ReadPixelFormat(s);
     Header.dwCaps = ReadUInt(s);
     Header.dwCaps2 = ReadUInt(s);
     Header.dwCaps3 = ReadUInt(s);
     Header.dwCaps4 = ReadUInt(s);
     Header.dwReserved2 = ReadUInt(s);
     if (Header.ddspf.dwFourCC == 0x30315844)
     {
         Header.extension = new DDS_HEADER_DXT10();
         Header.extension.dxgiFormat = (DXGI_FORMAT)ReadUInt(s);
         Header.extension.resourceDimension = (D3D10_RESOURCE_DIMENSION)ReadUInt(s);
         Header.extension.miscFlag = ReadUInt(s);
         Header.extension.arraySize = ReadUInt(s);
         Header.extension.miscFlags2 = (ALPHA_MODE)ReadUInt(s);
     }
 }
Example #13
0
        /// <summary>
        /// Internal read implementation of the sub classes.
        /// </summary>
        /// <param name="reader"></param>
        /// <exception cref="Exception">Expected DDS RGB24 or RGBA32 color format!</exception>
        /// <exception cref="NotImplementedException">TODO</exception>
        protected override void _Read(BinaryReader reader)
        {
            long baseOffset = reader.BaseStream.Position;

            int gbixOffset = 0;
            int pvrtOffset = 0;

            uint identifier = reader.ReadUInt32();

            if (identifier == m_gbix) //"GBIX"
            {
                HasGlobalIndex  = true;
                GlobalIndexSize = reader.ReadUInt32();
                GlobalIndex     = reader.ReadBytes((int)GlobalIndexSize);

                reader.BaseStream.Seek(4, SeekOrigin.Current); //Skip "PVRT"
                gbixOffset = 0x00;
                pvrtOffset = 0x08 + (int)GlobalIndexSize;
            }
            else
            {
                identifier = reader.ReadUInt32();
                if (identifier == m_gbix)
                {
                    HasGlobalIndex  = true;
                    GlobalIndexSize = reader.ReadUInt32();
                    GlobalIndex     = reader.ReadBytes((int)GlobalIndexSize);
                    gbixOffset      = 0x04;
                    pvrtOffset      = 0x0C + (int)GlobalIndexSize;
                }
                else if (identifier == m_pvrt)
                {
                    gbixOffset = -1;
                    pvrtOffset = 0x04;
                }
                else
                {
                    gbixOffset = -1;
                    pvrtOffset = 0x00;
                    reader.BaseStream.Seek(-4, SeekOrigin.Current);
                }
            }

            // Read information about the texture
            ContentSize = reader.ReadUInt32();
            PixelFormat = (PvrPixelFormat)reader.ReadByte();
            DataFormat  = (PvrDataFormat)reader.ReadByte();
            reader.BaseStream.Seek(2, SeekOrigin.Current);
            Width  = reader.ReadUInt16();
            Height = reader.ReadUInt16();

            if (DataFormat == PvrDataFormat.DDS || DataFormat == PvrDataFormat.DDS_2)
            {
                if (!(PixelFormat == PvrPixelFormat.DDS_DXT1_RGB24 || PixelFormat == PvrPixelFormat.DDS_DXT3_RGBA32))
                {
                    throw new Exception("Expected DDS RGB24 or RGBA32 color format!");
                }

                long             ddsOffset = reader.BaseStream.Position;
                DDS_Header       header    = new DDS_Header(reader.BaseStream);
                DDSFormatDetails format    = new DDSFormatDetails(header.Format, header.DX10_DXGI_AdditionalHeader.dxgiFormat);
                reader.BaseStream.Seek(ddsOffset, SeekOrigin.Begin);

                byte[] ddsBuffer = reader.ReadBytes(header.dwPitchOrLinearSize + header.dwSize + 128);

                MemoryStream memoryStream = new MemoryStream(ddsBuffer, 0, ddsBuffer.Length, true, true);
                MipMaps = DDSGeneral.LoadDDS(memoryStream, header, 0, format);
                memoryStream.Close();

                Width  = header.Width;
                Height = header.Height;
            }
            else
            {
                // Get the codecs and make sure we can decode using them
                PixelCodec = PvrPixelCodec.GetPixelCodec(PixelFormat);
                DataCodec  = PvrDataCodec.GetDataCodec(DataFormat);
                if (DataCodec != null && PixelCodec != null)
                {
                    DataCodec.PixelCodec = PixelCodec;
                }

                // Set the number of palette entries
                int paletteEntries = DataCodec.PaletteEntries;
                if (DataFormat == PvrDataFormat.VECTOR_QUANTIZATION_SMALL || DataFormat == PvrDataFormat.VECTOR_QUANTIZATION_SMALL_MIPMAP)
                {
                    if (Width <= 16)
                    {
                        paletteEntries = 64; // Actually 16
                    }
                    else if (Width <= 32)
                    {
                        paletteEntries = 256; // Actually 64
                    }
                    else if (Width <= 64)
                    {
                        paletteEntries = 512; // Actually 128
                    }
                    else
                    {
                        paletteEntries = 1024; // Actually 256
                    }
                }

                // Set the palette and data offsets
                int paletteOffset = 0;
                int dataOffset    = 0;
                if (paletteEntries == 0 || DataCodec.NeedsExternalPalette)
                {
                    paletteOffset = -1;
                    dataOffset    = pvrtOffset + 0x10;
                }
                else
                {
                    paletteOffset = pvrtOffset + 0x10;
                    dataOffset    = paletteOffset + (paletteEntries * (PixelCodec.Bpp >> 3));
                }

                // Get the compression format and determine if we need to decompress this texture
                reader.BaseStream.Seek(baseOffset, SeekOrigin.Begin);
                uint first = reader.ReadUInt32();
                reader.BaseStream.Seek(baseOffset + pvrtOffset + 4, SeekOrigin.Begin);
                uint second = reader.ReadUInt32();
                if (first == second - pvrtOffset + dataOffset + 8)
                {
                    CompressionFormat = PvrCompressionFormat.RLE;
                }
                else
                {
                    CompressionFormat = PvrCompressionFormat.NONE;
                }
                CompressionCodec = PvrCompressionCodec.GetCompressionCodec(CompressionFormat);

                if (CompressionFormat != PvrCompressionFormat.NONE && CompressionCodec != null)
                {
                    //TODO: Convert to stream compatible code
                    throw new NotImplementedException("TODO");
                    //m_encodedData = CompressionCodec.Decompress(m_encodedData, dataOffset, PixelCodec, DataCodec);

                    // Now place the offsets in the appropiate area
                    if (CompressionFormat == PvrCompressionFormat.RLE)
                    {
                        if (gbixOffset != -1)
                        {
                            gbixOffset -= 4;
                        }
                        pvrtOffset -= 4;
                        if (paletteOffset != -1)
                        {
                            paletteOffset -= 4;
                        }
                        dataOffset -= 4;
                    }
                }

                // If the texture contains mipmaps, gets the offsets of them
                int[] mipmapOffsets;
                if (DataCodec.HasMipmaps)
                {
                    int mipmapOffset = 0;
                    mipmapOffsets = new int[(int)Math.Log(Width, 2) + 1];

                    // Calculate the padding for the first mipmap offset
                    if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP)
                    {
                        mipmapOffset = (DataCodec.Bpp) >> 3; // A 1x1 mipmap takes up as much space as a 2x1 mipmap
                    }
                    else if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP_ALT)
                    {
                        mipmapOffset = (3 * DataCodec.Bpp) >> 3; // A 1x1 mipmap takes up as much space as a 2x2 mipmap
                    }
                    for (int i = mipmapOffsets.Length - 1, size = 1; i >= 0; i--, size <<= 1)
                    {
                        mipmapOffsets[i] = mipmapOffset;
                        mipmapOffset    += Math.Max((size * size * DataCodec.Bpp) >> 3, 1);
                    }
                }
                else
                {
                    mipmapOffsets = new int[1] {
                        0
                    };
                }

                //DecodeMipmaps()
                if (paletteOffset != -1) // The texture contains an embedded palette
                {
                    reader.BaseStream.Seek(baseOffset + paletteOffset, SeekOrigin.Begin);
                    DataCodec.SetPalette(reader, paletteEntries);
                }

                MipMaps = new List <MipMap>();
                if (DataCodec.HasMipmaps)
                {
                    for (int i = 0, size = Width; i < mipmapOffsets.Length; i++, size >>= 1)
                    {
                        reader.BaseStream.Seek(baseOffset + dataOffset + mipmapOffsets[i], SeekOrigin.Begin);
                        byte[] pixels = DataCodec.Decode(reader, size, size, PixelCodec);
                        MipMaps.Add(new MipMap(pixels, size, size));
                    }
                }
                else
                {
                    reader.BaseStream.Seek(baseOffset + dataOffset + mipmapOffsets[0], SeekOrigin.Begin);
                    byte[] pixels = DataCodec.Decode(reader, Width, Height, PixelCodec);
                    MipMaps.Add(new MipMap(pixels, Width, Height));
                }
            }
            if (HasGlobalIndex)
            {
                reader.BaseStream.Seek(baseOffset + ContentSize + 0xC, SeekOrigin.Begin);
            }
            else
            {
                reader.BaseStream.Seek(baseOffset + ContentSize, SeekOrigin.Begin);
            }
        }
        byte[] AttemptSaveUsingOriginalData(ImageFormats.ImageEngineFormatDetails destFormatDetails, MipHandling GenerateMips, int desiredMaxDimension, int mipToSave, AlphaSettings alphaSetting)
        {
            int        start      = 0;
            int        destStart  = 0;
            int        length     = OriginalData.Length;
            int        newWidth   = Width;
            int        newHeight  = Height;
            DDS_Header tempHeader = null;

            byte[] data             = null;
            byte[] tempOriginalData = OriginalData;

            if (destFormatDetails.IsDDS)
            {
                destStart = destFormatDetails.HeaderSize;
                start     = destStart;

                int mipCount = 0;

                if (mipToSave != 0)
                {
                    mipCount  = 1;
                    newWidth  = MipMaps[mipToSave].Width;
                    newHeight = MipMaps[mipToSave].Height;

                    start  = ImageFormats.GetCompressedSize(mipToSave, destFormatDetails, Width, Height);
                    length = ImageFormats.GetCompressedSize(1, destFormatDetails, newWidth, newHeight);
                }
                else if (desiredMaxDimension != 0 && desiredMaxDimension < Width && desiredMaxDimension < Height)
                {
                    int index = MipMaps.FindIndex(t => t.Width < desiredMaxDimension && t.Height < desiredMaxDimension);

                    // If none found, do a proper save and see what happens.
                    if (index == -1)
                    {
                        return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave));
                    }

                    mipCount -= index;
                    newWidth  = MipMaps[index].Width;
                    newHeight = MipMaps[index].Height;

                    start  = ImageFormats.GetCompressedSize(index, destFormatDetails, Width, Height);
                    length = ImageFormats.GetCompressedSize(mipCount, destFormatDetails, newWidth, newHeight);
                }
                else
                {
                    if (alphaSetting == AlphaSettings.RemoveAlphaChannel)
                    {
                        // Can't edit alpha directly in premultiplied formats. Not easily anyway.
                        if (destFormatDetails.IsPremultipliedFormat)
                        {
                            return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave));
                        }


                        // DDS Formats only
                        switch (destFormatDetails.Format)
                        {
                        // Excluded cos they have no true alpha
                        case ImageEngineFormat.DDS_A8:
                        case ImageEngineFormat.DDS_A8L8:
                        case ImageEngineFormat.DDS_ATI1:
                        case ImageEngineFormat.DDS_ATI2_3Dc:
                        case ImageEngineFormat.DDS_V8U8:
                        case ImageEngineFormat.DDS_G16_R16:
                        case ImageEngineFormat.DDS_G8_L8:
                        case ImageEngineFormat.DDS_R5G6B5:
                        case ImageEngineFormat.DDS_RGB_8:
                        case ImageEngineFormat.DDS_DXT1:
                            break;

                        // Exluded cos they're alpha isn't easily edited
                        case ImageEngineFormat.DDS_DXT2:
                        case ImageEngineFormat.DDS_DXT4:
                            break;

                        // Excluded cos they're currently unsupported
                        case ImageEngineFormat.DDS_CUSTOM:
                        case ImageEngineFormat.DDS_DX10:
                        case ImageEngineFormat.DDS_ARGB_4:
                            break;

                        case ImageEngineFormat.DDS_ABGR_8:
                        case ImageEngineFormat.DDS_ARGB_32F:
                        case ImageEngineFormat.DDS_ARGB_8:
                        case ImageEngineFormat.DDS_DXT3:
                        case ImageEngineFormat.DDS_DXT5:
                            tempOriginalData = new byte[OriginalData.Length];
                            Array.Copy(OriginalData, tempOriginalData, OriginalData.Length);

                            // Edit alpha values
                            int    alphaStart = 128;
                            int    alphaJump  = 0;
                            byte[] alphaBlock = null;
                            if (destFormatDetails.IsBlockCompressed)
                            {
                                alphaJump  = 16;
                                alphaBlock = new byte[8];
                                for (int i = 0; i < 8; i++)
                                {
                                    alphaBlock[i] = 255;
                                }
                            }
                            else
                            {
                                alphaJump  = destFormatDetails.ComponentSize * 4;
                                alphaBlock = new byte[destFormatDetails.ComponentSize];

                                switch (destFormatDetails.ComponentSize)
                                {
                                case 1:
                                    alphaBlock[0] = 255;
                                    break;

                                case 2:
                                    alphaBlock = BitConverter.GetBytes(ushort.MaxValue);
                                    break;

                                case 4:
                                    alphaBlock = BitConverter.GetBytes(1f);
                                    break;
                                }
                            }

                            for (int i = alphaStart; i < OriginalData.Length; i += alphaJump)
                            {
                                Array.Copy(alphaBlock, 0, tempOriginalData, i, alphaBlock.Length);
                            }

                            break;
                        }
                    }


                    switch (GenerateMips)
                    {
                    case MipHandling.KeepExisting:
                        mipCount = NumMipMaps;
                        break;

                    case MipHandling.Default:
                        if (NumMipMaps > 1)
                        {
                            mipCount = NumMipMaps;
                        }
                        else
                        {
                            goto case MipHandling.GenerateNew;      // Eww goto...
                        }
                        break;

                    case MipHandling.GenerateNew:
                        ImageEngine.DestroyMipMaps(MipMaps);
                        ImageEngine.TestDDSMipSize(MipMaps, destFormatDetails, Width, Height, out double fixXScale, out double fixYScale, GenerateMips);

                        // Wrong sizing, so can't use original data anyway.
                        if (fixXScale != 0 || fixYScale != 0)
                        {
                            return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave));
                        }


                        mipCount = DDSGeneral.BuildMipMaps(MipMaps);

                        // Compress mipmaps excl top
                        byte[] formattedMips = DDSGeneral.Save(MipMaps.GetRange(1, MipMaps.Count - 1), destFormatDetails, alphaSetting);
                        if (formattedMips == null)
                        {
                            return(null);
                        }

                        // Get top mip size and create destination array
                        length = ImageFormats.GetCompressedSize(0, destFormatDetails, newWidth, newHeight);     // Should be the length of the top mipmap.
                        data   = new byte[formattedMips.Length + length];

                        // Copy smaller mips to destination
                        Array.Copy(formattedMips, destFormatDetails.HeaderSize, data, length, formattedMips.Length - destFormatDetails.HeaderSize);
                        break;

                    case MipHandling.KeepTopOnly:
                        mipCount = 1;
                        length   = ImageFormats.GetCompressedSize(1, destFormatDetails, newWidth, newHeight);
                        break;
                    }
                }

                // Header
                tempHeader = new DDS_Header(mipCount, newHeight, newWidth, destFormatDetails.Format, destFormatDetails.DX10Format);
            }

            // Use existing array, otherwise create one.
            data = data ?? new byte[length];
            Array.Copy(tempOriginalData, start, data, destStart, length - destStart);

            // Write header if existing (DDS Only)
            if (tempHeader != null)
            {
                tempHeader.WriteToArray(data, 0);
            }

            return(data);
        }
Example #15
0
        public static byte[] DecodeToDDS(GNFTexture texture)
        {
            var imageFormat     = ImageEngineFormat.DDS_DXT5;
            var dx10ImageFormat = DDS_Header.DXGI_FORMAT.DXGI_FORMAT_UNKNOWN;

            switch (texture.SurfaceFormat)
            {
            case GNF.SurfaceFormat.BC1:
                imageFormat = ImageEngineFormat.DDS_DXT1;
                break;

            case GNF.SurfaceFormat.BC2:
                imageFormat = ImageEngineFormat.DDS_DXT2;
                break;

            case GNF.SurfaceFormat.BC3:
                imageFormat = ImageEngineFormat.DDS_DXT5;
                break;

            case GNF.SurfaceFormat.BC4:
                imageFormat = ImageEngineFormat.DDS_ATI1;
                break;

            case GNF.SurfaceFormat.BC5:
                imageFormat = ImageEngineFormat.DDS_ATI2_3Dc;
                break;

            case GNF.SurfaceFormat.BC6:
                imageFormat     = ImageEngineFormat.DDS_DX10;
                dx10ImageFormat = DDS_Header.DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16;
                break;

            case GNF.SurfaceFormat.BC7:
                imageFormat = ImageEngineFormat.DDS_DX10;

                switch (texture.ChannelType)
                {
                case ChannelType.Srgb:
                    dx10ImageFormat = DDS_Header.DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB;
                    break;

                default:
                    dx10ImageFormat = DDS_Header.DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM;
                    break;
                }
                break;
            }

            var ddsHeaderSize = 0x80;

            if (dx10ImageFormat != DDS_Header.DXGI_FORMAT.DXGI_FORMAT_UNKNOWN)
            {
                ddsHeaderSize += 20;
            }

            var ddsBytes = new byte[ddsHeaderSize + texture.Data.Length];

            // create & write header
            var ddsHeader = new DDS_Header(1, texture.Height, texture.Width, imageFormat, dx10ImageFormat);

            ddsHeader.WriteToArray(ddsBytes, 0);

            // unswizzle
            var data = Swizzler.UnSwizzle(texture.Data, texture.Width, texture.Height, imageFormat == ImageEngineFormat.DDS_DXT1 ? 8 : 16, SwizzleType.PS4);

            // write pixel data
            Array.Copy(data, 0, ddsBytes, ddsHeaderSize, texture.Data.Length);

            return(ddsBytes);
        }