[1114] | 1 | using System;
|
---|
| 2 | using System.Collections.Generic;
|
---|
| 3 | using System.IO;
|
---|
| 4 |
|
---|
| 5 | namespace Oni.Imaging
|
---|
| 6 | {
|
---|
| 7 | internal class DdsHeader
|
---|
| 8 | {
|
---|
| 9 | #region Private data
|
---|
| 10 |
|
---|
| 11 | private enum FOURCC
|
---|
| 12 | {
|
---|
| 13 | FOURCC_NONE = 0,
|
---|
| 14 | FOURCC_DXT1 = 0x31545844
|
---|
| 15 | }
|
---|
| 16 |
|
---|
| 17 | [Flags]
|
---|
| 18 | private enum DDS_FLAGS
|
---|
| 19 | {
|
---|
| 20 | DDSD_CAPS = 0x00000001,
|
---|
| 21 | DDSD_HEIGHT = 0x00000002,
|
---|
| 22 | DDSD_WIDTH = 0x00000004,
|
---|
| 23 | DDSD_PITCH = 0x00000008,
|
---|
| 24 | DDSD_PIXELFORMAT = 0x00001000,
|
---|
| 25 | DDSD_MIPMAPCOUNT = 0x00020000,
|
---|
| 26 | DDSD_LINEARSIZE = 0x00080000,
|
---|
| 27 | DDSD_DEPTH = 0x00800000
|
---|
| 28 | }
|
---|
| 29 |
|
---|
| 30 | [Flags]
|
---|
| 31 | private enum DDP_FLAGS
|
---|
| 32 | {
|
---|
| 33 | DDPF_RGB = 0x00000040,
|
---|
| 34 | DDPF_FOURCC = 0x00000004,
|
---|
| 35 | DDPF_ALPHAPIXELS = 0x00000001
|
---|
| 36 | }
|
---|
| 37 |
|
---|
| 38 | [Flags]
|
---|
| 39 | private enum DDS_CAPS
|
---|
| 40 | {
|
---|
| 41 | DDSCAPS_TEXTURE = 0x00001000,
|
---|
| 42 | DDSCAPS_MIPMAP = 0x00400000,
|
---|
| 43 | DDSCAPS_COMPLEX = 0x00000008
|
---|
| 44 | }
|
---|
| 45 |
|
---|
| 46 | [Flags]
|
---|
| 47 | private enum DDS_CAPS2
|
---|
| 48 | {
|
---|
| 49 | DDSCAPS2_CUBEMAP = 0x00000200,
|
---|
| 50 | DDSCAPS2_VOLUME = 0x00200000
|
---|
| 51 | }
|
---|
| 52 |
|
---|
| 53 | private const int DDS_MAGIC = 0x20534444;
|
---|
| 54 |
|
---|
| 55 | private DDS_FLAGS flags;
|
---|
| 56 | private int height;
|
---|
| 57 | private int width;
|
---|
| 58 | private int linearSize;
|
---|
| 59 | private int depth;
|
---|
| 60 | private int mipmapCount;
|
---|
| 61 | private DDP_FLAGS formatFlags;
|
---|
| 62 | private FOURCC fourCC;
|
---|
| 63 | private int rgbBitCount;
|
---|
| 64 | private uint rBitMask;
|
---|
| 65 | private uint gBitMask;
|
---|
| 66 | private uint bBitMask;
|
---|
| 67 | private uint aBitMask;
|
---|
| 68 | private DDS_CAPS caps;
|
---|
| 69 | private DDS_CAPS2 caps2;
|
---|
| 70 | #endregion
|
---|
| 71 |
|
---|
| 72 | public int Width => width;
|
---|
| 73 | public int Height => height;
|
---|
| 74 | public int MipmapCount => mipmapCount;
|
---|
| 75 |
|
---|
| 76 | public SurfaceFormat GetSurfaceFormat()
|
---|
| 77 | {
|
---|
| 78 | if (fourCC == FOURCC.FOURCC_DXT1)
|
---|
| 79 | return SurfaceFormat.DXT1;
|
---|
| 80 |
|
---|
| 81 | if (rgbBitCount == 32)
|
---|
| 82 | {
|
---|
| 83 | if (rBitMask == 0x00ff0000 && gBitMask == 0x0000ff00 && bBitMask == 0x000000ff)
|
---|
| 84 | {
|
---|
| 85 | if ((formatFlags & DDP_FLAGS.DDPF_ALPHAPIXELS) == 0)
|
---|
| 86 | return SurfaceFormat.BGRX;
|
---|
| 87 |
|
---|
| 88 | if (aBitMask == 0xff000000)
|
---|
| 89 | return SurfaceFormat.BGRA;
|
---|
| 90 | }
|
---|
| 91 | }
|
---|
| 92 | else if (rgbBitCount == 16)
|
---|
| 93 | {
|
---|
| 94 | if (rBitMask == 0x7c00 && gBitMask == 0x03e0 && bBitMask == 0x001f)
|
---|
| 95 | {
|
---|
| 96 | if ((formatFlags & DDP_FLAGS.DDPF_ALPHAPIXELS) == 0)
|
---|
| 97 | return SurfaceFormat.BGRX5551;
|
---|
| 98 |
|
---|
| 99 | if (aBitMask == 0x8000)
|
---|
| 100 | return SurfaceFormat.BGRA5551;
|
---|
| 101 | }
|
---|
| 102 | else if (rBitMask == 0x0f00 && gBitMask == 0x00f0 && bBitMask == 0x000f)
|
---|
| 103 | {
|
---|
| 104 | if ((formatFlags & DDP_FLAGS.DDPF_ALPHAPIXELS) != 0)
|
---|
| 105 | return SurfaceFormat.BGRA4444;
|
---|
| 106 | }
|
---|
| 107 | }
|
---|
| 108 |
|
---|
| 109 | throw new NotSupportedException(string.Format("Unsupported pixel format {0} {1} {2} {3} {4} {5} {6}",
|
---|
| 110 | formatFlags,
|
---|
| 111 | fourCC, rgbBitCount,
|
---|
| 112 | rBitMask, gBitMask, bBitMask, aBitMask));
|
---|
| 113 | }
|
---|
| 114 |
|
---|
| 115 | public static DdsHeader Read(BinaryReader reader)
|
---|
| 116 | {
|
---|
| 117 | if (reader.ReadInt32() != DDS_MAGIC)
|
---|
| 118 | throw new InvalidDataException("Not a DDS file");
|
---|
| 119 |
|
---|
| 120 | var header = new DdsHeader();
|
---|
| 121 |
|
---|
| 122 | if (reader.ReadInt32() != 124)
|
---|
| 123 | throw new InvalidDataException("Invalid DDS header size");
|
---|
| 124 |
|
---|
| 125 | header.flags = (DDS_FLAGS)reader.ReadInt32();
|
---|
| 126 |
|
---|
| 127 | var requiredFlags = DDS_FLAGS.DDSD_CAPS | DDS_FLAGS.DDSD_HEIGHT | DDS_FLAGS.DDSD_WIDTH | DDS_FLAGS.DDSD_PIXELFORMAT;
|
---|
| 128 |
|
---|
| 129 | if ((header.flags & requiredFlags) != requiredFlags)
|
---|
| 130 | throw new InvalidDataException(string.Format("Invalid DDS header flags ({0})", header.flags));
|
---|
| 131 |
|
---|
| 132 | header.height = reader.ReadInt32();
|
---|
| 133 | header.width = reader.ReadInt32();
|
---|
| 134 |
|
---|
| 135 | if (header.width == 0 || header.height == 0)
|
---|
| 136 | throw new InvalidDataException("DDS file has 0 width or height");
|
---|
| 137 |
|
---|
| 138 | header.linearSize = reader.ReadInt32();
|
---|
| 139 | header.depth = reader.ReadInt32();
|
---|
| 140 |
|
---|
| 141 | if ((header.flags & DDS_FLAGS.DDSD_MIPMAPCOUNT) != 0)
|
---|
| 142 | {
|
---|
| 143 | header.mipmapCount = reader.ReadInt32();
|
---|
| 144 | }
|
---|
| 145 | else
|
---|
| 146 | {
|
---|
| 147 | reader.ReadInt32();
|
---|
| 148 | header.mipmapCount = 1;
|
---|
| 149 | }
|
---|
| 150 |
|
---|
| 151 | reader.Position += 44;
|
---|
| 152 |
|
---|
| 153 | if (reader.ReadInt32() != 32)
|
---|
| 154 | throw new InvalidDataException("Invalid DDS pixel format size");
|
---|
| 155 |
|
---|
| 156 | header.formatFlags = (DDP_FLAGS)reader.ReadInt32();
|
---|
| 157 |
|
---|
| 158 | if ((header.formatFlags & DDP_FLAGS.DDPF_FOURCC) != 0)
|
---|
| 159 | {
|
---|
| 160 | header.fourCC = (FOURCC)reader.ReadInt32();
|
---|
| 161 | }
|
---|
| 162 | else
|
---|
| 163 | {
|
---|
| 164 | reader.ReadInt32();
|
---|
| 165 | header.fourCC = FOURCC.FOURCC_NONE;
|
---|
| 166 | }
|
---|
| 167 |
|
---|
| 168 | header.rgbBitCount = reader.ReadInt32();
|
---|
| 169 | header.rBitMask = reader.ReadUInt32();
|
---|
| 170 | header.gBitMask = reader.ReadUInt32();
|
---|
| 171 | header.bBitMask = reader.ReadUInt32();
|
---|
| 172 | header.aBitMask = reader.ReadUInt32();
|
---|
| 173 |
|
---|
| 174 | header.caps = (DDS_CAPS)reader.ReadInt32();
|
---|
| 175 | header.caps2 = (DDS_CAPS2)reader.ReadInt32();
|
---|
| 176 |
|
---|
| 177 | reader.Position += 12;
|
---|
| 178 |
|
---|
| 179 | if (header.fourCC == FOURCC.FOURCC_NONE)
|
---|
| 180 | {
|
---|
| 181 | if (header.rgbBitCount != 16 && header.rgbBitCount != 32)
|
---|
| 182 | throw new NotSupportedException(string.Format("Unsupported RGB bit count {0}", header.rgbBitCount));
|
---|
| 183 | }
|
---|
| 184 | else if (header.fourCC != FOURCC.FOURCC_DXT1)
|
---|
| 185 | {
|
---|
| 186 | throw new NotSupportedException(string.Format("Unsupported FOURCC {0}", header.fourCC));
|
---|
| 187 | }
|
---|
| 188 |
|
---|
| 189 | return header;
|
---|
| 190 | }
|
---|
| 191 |
|
---|
| 192 | public static DdsHeader Create(IList<Surface> surfaces)
|
---|
| 193 | {
|
---|
| 194 | var header = new DdsHeader();
|
---|
| 195 |
|
---|
| 196 | int width = surfaces[0].Width;
|
---|
| 197 | int height = surfaces[0].Height;
|
---|
| 198 | var format = surfaces[0].Format;
|
---|
| 199 |
|
---|
| 200 | header.flags = DDS_FLAGS.DDSD_CAPS | DDS_FLAGS.DDSD_HEIGHT | DDS_FLAGS.DDSD_WIDTH | DDS_FLAGS.DDSD_PIXELFORMAT;
|
---|
| 201 | header.width = width;
|
---|
| 202 | header.height = height;
|
---|
| 203 | header.caps = DDS_CAPS.DDSCAPS_TEXTURE;
|
---|
| 204 |
|
---|
| 205 | switch (format)
|
---|
| 206 | {
|
---|
| 207 | case SurfaceFormat.BGRA4444:
|
---|
| 208 | header.formatFlags = DDP_FLAGS.DDPF_RGB | DDP_FLAGS.DDPF_ALPHAPIXELS;
|
---|
| 209 | header.rgbBitCount = 16;
|
---|
| 210 | header.aBitMask = 0xf000;
|
---|
| 211 | header.rBitMask = 0x0f00;
|
---|
| 212 | header.gBitMask = 0x00f0;
|
---|
| 213 | header.bBitMask = 0x000f;
|
---|
| 214 | break;
|
---|
| 215 | case SurfaceFormat.BGRX5551:
|
---|
| 216 | case SurfaceFormat.BGRA5551:
|
---|
| 217 | header.formatFlags = DDP_FLAGS.DDPF_RGB | DDP_FLAGS.DDPF_ALPHAPIXELS;
|
---|
| 218 | header.rgbBitCount = 16;
|
---|
| 219 | header.aBitMask = 0x8000;
|
---|
| 220 | header.rBitMask = 0x7c00;
|
---|
| 221 | header.gBitMask = 0x03e0;
|
---|
| 222 | header.bBitMask = 0x001f;
|
---|
| 223 | break;
|
---|
| 224 | case SurfaceFormat.BGRA:
|
---|
| 225 | header.formatFlags = DDP_FLAGS.DDPF_RGB | DDP_FLAGS.DDPF_ALPHAPIXELS;
|
---|
| 226 | header.rgbBitCount = 32;
|
---|
| 227 | header.aBitMask = 0xff000000;
|
---|
| 228 | header.rBitMask = 0x00ff0000;
|
---|
| 229 | header.gBitMask = 0x0000ff00;
|
---|
| 230 | header.bBitMask = 0x000000ff;
|
---|
| 231 | break;
|
---|
| 232 | case SurfaceFormat.BGRX:
|
---|
| 233 | header.formatFlags = DDP_FLAGS.DDPF_RGB;
|
---|
| 234 | header.rgbBitCount = 32;
|
---|
| 235 | header.rBitMask = 0x00ff0000;
|
---|
| 236 | header.gBitMask = 0x0000ff00;
|
---|
| 237 | header.bBitMask = 0x000000ff;
|
---|
| 238 | break;
|
---|
| 239 | case SurfaceFormat.RGBA:
|
---|
| 240 | header.formatFlags = DDP_FLAGS.DDPF_RGB | DDP_FLAGS.DDPF_ALPHAPIXELS;
|
---|
| 241 | header.rgbBitCount = 32;
|
---|
| 242 | header.aBitMask = 0x000000ff;
|
---|
| 243 | header.rBitMask = 0x0000ff00;
|
---|
| 244 | header.gBitMask = 0x00ff0000;
|
---|
| 245 | header.bBitMask = 0xff000000;
|
---|
| 246 | break;
|
---|
| 247 | case SurfaceFormat.RGBX:
|
---|
| 248 | header.formatFlags = DDP_FLAGS.DDPF_RGB;
|
---|
| 249 | header.rgbBitCount = 32;
|
---|
| 250 | header.rBitMask = 0x0000ff00;
|
---|
| 251 | header.gBitMask = 0x00ff0000;
|
---|
| 252 | header.bBitMask = 0xff000000;
|
---|
| 253 | break;
|
---|
| 254 | case SurfaceFormat.DXT1:
|
---|
| 255 | header.formatFlags = DDP_FLAGS.DDPF_FOURCC;
|
---|
| 256 | header.fourCC = FOURCC.FOURCC_DXT1;
|
---|
| 257 | break;
|
---|
| 258 | }
|
---|
| 259 |
|
---|
| 260 | switch (format)
|
---|
| 261 | {
|
---|
| 262 | case SurfaceFormat.BGRA4444:
|
---|
| 263 | case SurfaceFormat.BGRX5551:
|
---|
| 264 | case SurfaceFormat.BGRA5551:
|
---|
| 265 | header.flags |= DDS_FLAGS.DDSD_PITCH;
|
---|
| 266 | header.linearSize = width * 2;
|
---|
| 267 | break;
|
---|
| 268 | case SurfaceFormat.BGRX:
|
---|
| 269 | case SurfaceFormat.BGRA:
|
---|
| 270 | case SurfaceFormat.RGBA:
|
---|
| 271 | case SurfaceFormat.RGBX:
|
---|
| 272 | header.flags |= DDS_FLAGS.DDSD_PITCH;
|
---|
| 273 | header.linearSize = width * 4;
|
---|
| 274 | break;
|
---|
| 275 | case SurfaceFormat.DXT1:
|
---|
| 276 | header.flags |= DDS_FLAGS.DDSD_LINEARSIZE;
|
---|
| 277 | header.linearSize = Math.Max(1, width / 4) * Math.Max(1, height / 4) * 8;
|
---|
| 278 | break;
|
---|
| 279 | }
|
---|
| 280 |
|
---|
| 281 | if (surfaces.Count > 1)
|
---|
| 282 | {
|
---|
| 283 | header.flags |= DDS_FLAGS.DDSD_MIPMAPCOUNT;
|
---|
| 284 | header.mipmapCount = surfaces.Count;
|
---|
| 285 | header.caps |= DDS_CAPS.DDSCAPS_COMPLEX | DDS_CAPS.DDSCAPS_MIPMAP;
|
---|
| 286 | }
|
---|
| 287 |
|
---|
| 288 | return header;
|
---|
| 289 | }
|
---|
| 290 |
|
---|
| 291 | public void Write(BinaryWriter writer)
|
---|
| 292 | {
|
---|
| 293 | writer.Write(DDS_MAGIC);
|
---|
| 294 | writer.Write(124);
|
---|
| 295 | writer.Write((int)flags);
|
---|
| 296 | writer.Write(height);
|
---|
| 297 | writer.Write(width);
|
---|
| 298 | writer.Write(linearSize);
|
---|
| 299 | writer.Write(depth);
|
---|
| 300 | writer.Write(mipmapCount);
|
---|
| 301 | writer.BaseStream.Seek(44, SeekOrigin.Current);
|
---|
| 302 | writer.Write(32);
|
---|
| 303 | writer.Write((int)formatFlags);
|
---|
| 304 | writer.Write((int)fourCC);
|
---|
| 305 | writer.Write(rgbBitCount);
|
---|
| 306 | writer.Write(rBitMask);
|
---|
| 307 | writer.Write(gBitMask);
|
---|
| 308 | writer.Write(bBitMask);
|
---|
| 309 | writer.Write(aBitMask);
|
---|
| 310 | writer.Write((int)caps);
|
---|
| 311 | writer.Write((int)caps2);
|
---|
| 312 | writer.BaseStream.Seek(12, SeekOrigin.Current);
|
---|
| 313 | }
|
---|
| 314 | }
|
---|
| 315 | }
|
---|