/// <summary>
        /// Gets the image row pitch (in bytes) and the slice pitch (size of the image in bytes) based
        /// on format, width, height and additional options.
        /// </summary>
        /// <param name="format">The texture format.</param>
        /// <param name="width">The image width in pixels.</param>
        /// <param name="height">The image height in pixels.</param>
        /// <param name="rowPitch">The row pitch in bytes.</param>
        /// <param name="slicePitch">The image size in bytes.</param>
        /// <param name="flags">Additional options.</param>
        public static void ComputePitch(DataFormat format, int width, int height, out int rowPitch, out int slicePitch, ComputePitchFlags flags)
        {
            switch (format)
              {
            case DataFormat.BC1_TYPELESS:
            case DataFormat.BC1_UNORM:
            case DataFormat.BC1_UNORM_SRGB:
            case DataFormat.BC4_TYPELESS:
            case DataFormat.BC4_UNORM:
            case DataFormat.BC4_SNORM:
              {
            Debug.Assert(IsBCn(format));

            // Width and height in blocks.
            int nbw = Math.Max(1, (width + 3) / 4);
            int nbh = Math.Max(1, (height + 3) / 4);

            rowPitch = nbw * 8;
            slicePitch = rowPitch * nbh;
              }
              break;

            case DataFormat.BC2_TYPELESS:
            case DataFormat.BC2_UNORM:
            case DataFormat.BC2_UNORM_SRGB:
            case DataFormat.BC3_TYPELESS:
            case DataFormat.BC3_UNORM:
            case DataFormat.BC3_UNORM_SRGB:
            case DataFormat.BC5_TYPELESS:
            case DataFormat.BC5_UNORM:
            case DataFormat.BC5_SNORM:
            case DataFormat.BC6H_TYPELESS:
            case DataFormat.BC6H_UF16:
            case DataFormat.BC6H_SF16:
            case DataFormat.BC7_TYPELESS:
            case DataFormat.BC7_UNORM:
            case DataFormat.BC7_UNORM_SRGB:
              {
            Debug.Assert(IsBCn(format));

            // Width and height in blocks.
            int nbw = Math.Max(1, (width + 3) / 4);
            int nbh = Math.Max(1, (height + 3) / 4);

            rowPitch = nbw * 16;
            slicePitch = rowPitch * nbh;
              }
              break;

            case DataFormat.PVRTCI_2bpp_RGB:
            case DataFormat.PVRTCI_2bpp_RGBA:
              {
            Debug.Assert(IsPvrtc(format));

            // Bytes per block.
            int bpb = 8;

            // Additional limitations set by Apple:
            // (Reference https://developer.apple.com/library/ios/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/TextureTool/TextureTool.html)
            //
            // - width and height must be power of two.
            // - width and height must be square.
            // - width and height must be at least 8.
            //   (PVRTexLib returns the same slice pitch for size = 1, 2, 4 and 8!)
            // - Minimum data size is 32 bytes (= 2x2 blocks).
            //   => Minimum size for 2 bpp mode: 16x8 pixels
            //      Minimum size for 4 bpp mode: 8x8 pixels.

            // 2 bpp mode: block = 8x4 pixels
            width = Math.Max(16, width);
            height = Math.Max(8, height);

            // Width and height in blocks.
            int nbw = Math.Max(1, (width + 7) / 8);
            int nbh = Math.Max(1, (height + 3) / 4);

            rowPitch = nbw * bpb;
            slicePitch = rowPitch * nbh;
              }
              break;

            case DataFormat.PVRTCI_4bpp_RGB:
            case DataFormat.PVRTCI_4bpp_RGBA:
              {
            Debug.Assert(IsPvrtc(format));

            // Bytes per block.
            int bpb = 8;

            // Additional limitations set by Apple:
            // (Reference https://developer.apple.com/library/ios/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/TextureTool/TextureTool.html)
            //
            // - width and height must be power of two.
            // - width and height must be square.
            // - width and height must be at least 8.
            //   (PVRTexLib returns the same slice pitch for size = 1, 2, 4 and 8!)
            // - Minimum data size is 32 bytes (= 2x2 blocks).
            //   => Minimum size for 2 bpp mode: 16x8 pixels
            //      Minimum size for 4 bpp mode: 8x8 pixels.

            // 4 bpp mode: block = 4x4 pixels
            width = Math.Max(8, width);
            height = Math.Max(8, height);

            // Width and height in blocks.
            int nbw = Math.Max(1, (width + 3) / 4);
            int nbh = Math.Max(1, (height + 3) / 4);

            rowPitch = nbw * bpb;
            slicePitch = rowPitch * nbh;
              }
              break;

            case DataFormat.ETC1:
              {
            Debug.Assert(IsEtc(format));

            // Reference: https://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt

            // Bytes per block.
            int bpb = 8;

            // Width and height in blocks.
            int nbw = Math.Max(1, (width + 3) / 4);
            int nbh = Math.Max(1, (height + 3) / 4);

            rowPitch = nbw * bpb;
            slicePitch = rowPitch * nbh;
              }
              break;

            case DataFormat.ATC_RGB:
              {
            Debug.Assert(IsAtc(format));

            // Reference: https://www.khronos.org/registry/gles/extensions/AMD/AMD_compressed_ATC_texture.txt

            // Width and height in blocks.
            int nbw = Math.Max(1, (width + 3) / 4);
            int nbh = Math.Max(1, (height + 3) / 4);

            rowPitch = nbw * 8;
            slicePitch = rowPitch * nbh;
              }
              break;

            case DataFormat.ATC_RGBA_EXPLICIT_ALPHA:
            case DataFormat.ATC_RGBA_INTERPOLATED_ALPHA:
              {
            Debug.Assert(IsAtc(format));

            // Reference: https://www.khronos.org/registry/gles/extensions/AMD/AMD_compressed_ATC_texture.txt

            // Width and height in blocks.
            int nbw = Math.Max(1, (width + 3) / 4);
            int nbh = Math.Max(1, (height + 3) / 4);

            rowPitch = nbw * 16;
            slicePitch = rowPitch * nbh;
              }
              break;

            case DataFormat.R8G8_B8G8_UNORM:
            case DataFormat.G8R8_G8B8_UNORM:
            case DataFormat.YUY2:
              {
            Debug.Assert(IsPacked(format));

            rowPitch = ((width + 1) >> 1) * 4;
            slicePitch = rowPitch * height;
              }
              break;

            case DataFormat.Y210: // 4:2:2 10-bit
            case DataFormat.Y216: // 4:2:2 16-bit
              {
            Debug.Assert(IsPacked(format));

            rowPitch = ((width + 1) >> 1) * 8;
            slicePitch = rowPitch * height;
              }
              break;

            case DataFormat.NV12:
            case DataFormat.Y420_OPAQUE:
              {
            Debug.Assert(IsPlanar(format));

            rowPitch = ((width + 1) >> 1) * 2;
            slicePitch = rowPitch * (height + ((height + 1) >> 1));
              }
              break;

            case DataFormat.P010:
            case DataFormat.P016:
            case DataFormat.D16_UNORM_S8_UINT:
            case DataFormat.R16_UNORM_X8_TYPELESS:
            case DataFormat.X16_TYPELESS_G8_UINT:
              {
            Debug.Assert(IsPlanar(format));

            rowPitch = ((width + 1) >> 1) * 4;
            slicePitch = rowPitch * (height + ((height + 1) >> 1));
              }
              break;

            case DataFormat.NV11:
              {
            Debug.Assert(IsPlanar(format));

            rowPitch = ((width + 3) >> 2) * 4;

            // Direct3D makes this simplifying assumption, although it is larger than the 4:1:1 data.
            slicePitch = rowPitch * height * 2;
              }
              break;

            case DataFormat.P208:
              {
            Debug.Assert(IsPlanar(format));

            rowPitch = ((width + 1) >> 1) * 2;
            slicePitch = rowPitch * height * 2;
              }
              break;

            case DataFormat.V208:
              {
            Debug.Assert(IsPlanar(format));

            rowPitch = width;
            slicePitch = rowPitch * (height + (((height + 1) >> 1) * 2));
              }
              break;

            case DataFormat.V408:
              {
            Debug.Assert(IsPlanar(format));

            rowPitch = width;
            slicePitch = rowPitch * (height + ((height >> 1) * 4));
              }
              break;

            default:
              {
            Debug.Assert(IsValid(format));
            Debug.Assert(!IsCompressed(format) && !IsPacked(format) && !IsPlanar(format));

            int bpp;

            if ((flags & ComputePitchFlags.Bpp24) != 0)
              bpp = 24;
            else if ((flags & ComputePitchFlags.Bpp16) != 0)
              bpp = 16;
            else if ((flags & ComputePitchFlags.Bpp8) != 0)
              bpp = 8;
            else
              bpp = BitsPerPixel(format);

            if ((flags & (ComputePitchFlags.LegacyDword | ComputePitchFlags.Paragraph | ComputePitchFlags.Ymm | ComputePitchFlags.Zmm | ComputePitchFlags.Page4K)) != 0)
            {
              if ((flags & ComputePitchFlags.Page4K) != 0)
              {
                rowPitch = ((width * bpp + 32767) / 32768) * 4096;
                slicePitch = rowPitch * height;
              }
              else if ((flags & ComputePitchFlags.Zmm) != 0)
              {
                rowPitch = ((width * bpp + 511) / 512) * 64;
                slicePitch = rowPitch * height;
              }
              else if ((flags & ComputePitchFlags.Ymm) != 0)
              {
                rowPitch = ((width * bpp + 255) / 256) * 32;
                slicePitch = rowPitch * height;
              }
              else if ((flags & ComputePitchFlags.Paragraph) != 0)
              {
                rowPitch = ((width * bpp + 127) / 128) * 16;
                slicePitch = rowPitch * height;
              }
              else // DWORD alignment
              {
                // Special computation for some incorrectly created DDS files based on
                // legacy DirectDraw assumptions about pitch alignment
                rowPitch = ((width * bpp + 31) / 32) * Marshal.SizeOf(typeof(uint));
                slicePitch = rowPitch * height;
              }
            }
            else
            {
              // Default byte alignment
              rowPitch = (width * bpp + 7) / 8;
              slicePitch = rowPitch * height;
            }
              }
              break;
              }
        }
Exemple #2
0
        private static Texture CopyImage(BinaryReader reader, TextureDescription description, ComputePitchFlags cpFlags, ConversionFlags convFlags, uint[] pal8)
        {
            if (reader == null)
            throw new ArgumentNullException("reader");

              if ((convFlags & ConversionFlags.Expand) != 0)
              {
            if ((convFlags & ConversionFlags.Format888) != 0)
              cpFlags |= ComputePitchFlags.Bpp24;
            else if ((convFlags & (ConversionFlags.Format565 | ConversionFlags.Format5551 | ConversionFlags.Format4444 | ConversionFlags.Format8332 | ConversionFlags.FormatA8P8 | ConversionFlags.FormatL16 | ConversionFlags.FormatA8L8)) != 0)
              cpFlags |= ComputePitchFlags.Bpp16;
            else if ((convFlags & (ConversionFlags.Format44 | ConversionFlags.Format332 | ConversionFlags.Pal8 | ConversionFlags.FormatL8)) != 0)
              cpFlags |= ComputePitchFlags.Bpp8;
              }

              var texture = new Texture(description);
              description = texture.Description;  // MipLevel may have been set.

              ScanlineFlags tflags = (convFlags & ConversionFlags.NoAlpha) != 0 ? ScanlineFlags.SetAlpha : 0;
              if ((convFlags & ConversionFlags.Swizzle) != 0)
            tflags |= ScanlineFlags.Legacy;

              switch (description.Dimension)
              {
            case TextureDimension.Texture1D:
            case TextureDimension.Texture2D:
            case TextureDimension.TextureCube:
              {
            int index = 0;
            for (int item = 0; item < description.ArraySize; ++item)
            {
              int width = description.Width;
              int height = description.Height;

              for (int level = 0; level < description.MipLevels; ++level, ++index)
              {
                int sRowPitch, sSlicePitch;
                TextureHelper.ComputePitch(description.Format, width, height, out sRowPitch, out sSlicePitch, cpFlags);

                var image = texture.Images[index];
                if (TextureHelper.IsBCn(description.Format) || TextureHelper.IsPlanar(description.Format))
                {
                  reader.Read(image.Data, 0, image.Data.Length);
                }
                else
                {
                  using (var stream = new MemoryStream(image.Data))
                  using (var writer = new BinaryWriter(stream))
                  {
                    for (int h = 0; h < height; ++h)
                    {
                      if ((convFlags & ConversionFlags.Expand) != 0)
                      {
                        if ((convFlags & (ConversionFlags.Format565 | ConversionFlags.Format5551 | ConversionFlags.Format4444)) != 0)
                        {
                          if (!TextureHelper.ExpandScanline(reader, sRowPitch, (convFlags & ConversionFlags.Format565) != 0 ? DataFormat.B5G6R5_UNORM : DataFormat.B5G5R5A1_UNORM, writer, image.RowPitch, DataFormat.R8G8B8A8_UNORM, tflags))
                            throw new InvalidDataException("Unable to expand format.");
                        }
                        else
                        {
                          LegacyFormat lformat = FindLegacyFormat(convFlags);
                          if (!LegacyExpandScanline(reader, sRowPitch, lformat, writer, image.RowPitch, description.Format, pal8, tflags))
                            throw new InvalidDataException("Unable to expand legacy format.");
                        }
                      }
                      else if ((convFlags & ConversionFlags.Swizzle) != 0)
                      {
                        TextureHelper.SwizzleScanline(reader, sRowPitch, writer, image.RowPitch, description.Format, tflags);
                      }
                      else
                      {
                        TextureHelper.CopyScanline(reader, sRowPitch, writer, image.RowPitch, description.Format, tflags);
                      }
                    }
                  }
                }

                if (width > 1)
                  width >>= 1;

                if (height > 1)
                  height >>= 1;
              }
            }
              }
              break;

            case TextureDimension.Texture3D:
              {
            int index = 0;

            int width = description.Width;
            int height = description.Height;
            int depth = description.Depth;

            for (int level = 0; level < description.MipLevels; ++level)
            {
              int sRowPitch, sSlicePitch;
              TextureHelper.ComputePitch(description.Format, width, height, out sRowPitch, out sSlicePitch, cpFlags);

              for (int slice = 0; slice < depth; ++slice, ++index)
              {
                // We use the same memory organization that Direct3D 11 needs for D3D11_SUBRESOURCE_DATA
                // with all slices of a given miplevel being continuous in memory
                var image = texture.Images[index];

                if (TextureHelper.IsBCn(description.Format))
                {
                  reader.Read(image.Data, 0, image.Data.Length);
                }
                else if (TextureHelper.IsPlanar(description.Format))
                {
                  // Direct3D does not support any planar formats for Texture3D
                  throw new NotSupportedException("Planar texture formats are not support for volume textures.");
                }
                else
                {
                  using (var stream = new MemoryStream(image.Data))
                  using (var writer = new BinaryWriter(stream))
                  {
                    for (int h = 0; h < height; ++h)
                    {
                      if ((convFlags & ConversionFlags.Expand) != 0)
                      {
                        if ((convFlags & (ConversionFlags.Format565 | ConversionFlags.Format5551 | ConversionFlags.Format4444)) != 0)
                        {
                          if (!TextureHelper.ExpandScanline(reader, sRowPitch, (convFlags & ConversionFlags.Format565) != 0 ? DataFormat.B5G6R5_UNORM : DataFormat.B5G5R5A1_UNORM, writer, image.RowPitch, DataFormat.R8G8B8A8_UNORM, tflags))
                            throw new InvalidDataException("Unable to expand format.");
                        }
                        else
                        {
                          LegacyFormat lformat = FindLegacyFormat(convFlags);
                          if (!LegacyExpandScanline(reader, sRowPitch, lformat, writer, image.RowPitch, description.Format, pal8, tflags))
                            throw new InvalidDataException("Unable to expand legacy format.");
                        }
                      }
                      else if ((convFlags & ConversionFlags.Swizzle) != 0)
                      {
                        TextureHelper.SwizzleScanline(reader, sRowPitch, writer, image.RowPitch, description.Format, tflags);
                      }
                      else
                      {
                        TextureHelper.CopyScanline(reader, sRowPitch, writer, image.RowPitch, description.Format, tflags);
                      }
                    }
                  }
                }
              }

              if (width > 1)
                width >>= 1;

              if (height > 1)
                height >>= 1;

              if (depth > 1)
                depth >>= 1;
            }
              }
              break;

            default:
              throw new NotSupportedException("The specified texture dimension is not supported.");
              }

              return texture;
        }