private static unsafe int WriteDdsHeader(ValveTextureFile vtf, int mip, byte[] buffer) { var header = new DdsHeader(); int blockSize; uint fourCC; switch (vtf.Header.HiResFormat) { case TextureFormat.DXT1: blockSize = 8; fourCC = 0x31545844; break; case TextureFormat.DXT3: blockSize = 16; fourCC = 0x33545844; break; case TextureFormat.DXT5: blockSize = 16; fourCC = 0x35545844; break; default: throw new NotImplementedException(); } header.dwWidth = (uint)Math.Max(1, vtf.Header.Width >> mip); header.dwHeight = (uint)Math.Max(1, vtf.Header.Height >> mip); header.dwSize = (uint)Marshal.SizeOf(typeof(DdsHeader)); header.dwFlags = DdsHeaderFlags.CAPS | DdsHeaderFlags.HEIGHT | DdsHeaderFlags.WIDTH | DdsHeaderFlags.PIXELFORMAT; header.dwPitchOrLinearSize = (uint)(Math.Max(1, (vtf.Header.Width + 3) / 4) * blockSize); header.dwDepth = 1; header.dwMipMapCount = 1; header.dwCaps = DdsCaps.TEXTURE; header.ddspf.dwSize = (uint)Marshal.SizeOf(typeof(DdsPixelFormat)); header.ddspf.dwFlags = DdsPixelFormatFlags.FOURCC; header.ddspf.dwFourCC = fourCC; fixed(byte *bufferPtr = buffer) { var magicPtr = (uint *)bufferPtr; var headerPtr = (DdsHeader *)(bufferPtr + sizeof(uint)); *magicPtr = 0x20534444; *headerPtr = header; } return((int)header.dwSize + sizeof(uint)); }
public static unsafe int WriteDdsHeader(ValveTextureFile VTF, int Mip, byte[] Buffer) { DdsHeader Header = new DdsHeader(); int BlockSize; uint FourCC; switch (VTF.Header.HiResFormat) { case TextureFormat.DXT1: BlockSize = 8; FourCC = 0x31545844; break; case TextureFormat.DXT3: BlockSize = 16; FourCC = 0x33545844; break; case TextureFormat.DXT5: BlockSize = 16; FourCC = 0x35545844; break; default: throw new NotImplementedException(); } Header.dwWidth = (uint)Math.Max(1, VTF.Header.Width >> Mip); Header.dwHeight = (uint)Math.Max(1, VTF.Header.Height >> Mip); Header.dwSize = (uint)Marshal.SizeOf(typeof(DdsHeader)); Header.dwFlags = DdsHeaderFlags.CAPS | DdsHeaderFlags.HEIGHT | DdsHeaderFlags.WIDTH | DdsHeaderFlags.PIXELFORMAT; Header.dwPitchOrLinearSize = (uint)(Math.Max(1, (VTF.Header.Width + 3) / 4) * BlockSize); Header.dwDepth = 1; Header.dwMipMapCount = 1; Header.dwCaps = DdsCaps.TEXTURE; Header.ddspf.dwSize = (uint)Marshal.SizeOf(typeof(DdsPixelFormat)); Header.ddspf.dwFlags = DdsPixelFormatFlags.FOURCC; Header.ddspf.dwFourCC = FourCC; fixed(byte *bufferPtr = Buffer) { uint * MagicPtr = (uint *)bufferPtr; DdsHeader *HeaderPtr = (DdsHeader *)(bufferPtr + sizeof(uint)); *MagicPtr = 0x20534444; *HeaderPtr = Header; } return((int)Header.dwSize + sizeof(uint)); }
private string GetPngUrl(HttpListenerRequest request, string path, ValveTextureFile vtf, string mapName = null) { var queryStringBuilder = new StringBuilder(); if (vtf.MipmapCount > 1) { AppendToQueryString(queryStringBuilder, "mipmap"); } if (vtf.FrameCount > 1) { AppendToQueryString(queryStringBuilder, "frame"); } if (vtf.FaceCount > 1) { AppendToQueryString(queryStringBuilder, "face"); } if (vtf.ZSliceCount > 1) { AppendToQueryString(queryStringBuilder, "zslice"); } return($"http://{request.Url.Authority}{UrlPrefix}/{GetProviderPrefix( mapName )}/{path.Replace( ".vtf", ".png" )}{queryStringBuilder}"); }
private JObject GetJson(IResourceProvider provider, string filePath, string mapName = null) { ValveTextureFile vtf; using (var vtfStream = provider.OpenFile(filePath)) { vtf = new ValveTextureFile(vtfStream, true); } var response = new JObject { { "width", vtf.Header.Width }, { "height", vtf.Header.Height }, { "version", vtf.Header.MajorVersion + vtf.Header.MinorVersion * 0.1f }, { "flags", (long)vtf.Header.Flags }, { "pngUrl", GetPngUrl(Request, filePath, vtf, mapName) }, { "mipmaps", vtf.MipmapCount }, { "frames", vtf.FrameCount }, { "faces", vtf.FaceCount }, { "depth", vtf.ZSliceCount } }; return(response); }
public static MagickImage DecodeImage(ValveTextureFile vtf, int mip, int frame, int face, int zslice) { var dataLength = vtf.GetHiResPixelDataLength(mip); var totalLength = dataLength + 128; if (_sPixelBuffer == null || _sPixelBuffer.Length < totalLength) { var powerOf2 = 256; while (powerOf2 < totalLength) { powerOf2 <<= 1; } _sPixelBuffer = new byte[powerOf2]; } var offset = 0; var width = Math.Max(1, vtf.Header.Width >> mip); var height = Math.Max(1, vtf.Header.Height >> mip); var readSettings = new MagickReadSettings { Width = width, Height = height }; switch (vtf.Header.HiResFormat) { case TextureFormat.DXT1: case TextureFormat.DXT3: case TextureFormat.DXT5: readSettings.Format = MagickFormat.Dds; offset = WriteDdsHeader(vtf, mip, _sPixelBuffer); break; case TextureFormat.BGR888: readSettings.PixelStorage = new PixelStorageSettings(StorageType.Char, "BGR"); break; case TextureFormat.RGB888: case TextureFormat.RGB888_BLUESCREEN: readSettings.PixelStorage = new PixelStorageSettings(StorageType.Char, "RGB"); break; case TextureFormat.ABGR8888: readSettings.PixelStorage = new PixelStorageSettings(StorageType.Char, "ABGR"); break; case TextureFormat.BGRA8888: readSettings.PixelStorage = new PixelStorageSettings(StorageType.Char, "BGRA"); break; case TextureFormat.RGBA8888: readSettings.PixelStorage = new PixelStorageSettings(StorageType.Char, "RGBA"); break; default: throw new NotImplementedException(); } vtf.GetHiResPixelData(mip, frame, face, zslice, _sPixelBuffer, offset); var img = new MagickImage(_sPixelBuffer, readSettings); if (img.Width != width || img.Height != height) { img.Resize(new MagickGeometry(width, height) { IgnoreAspectRatio = true }); } return(img); }
public static unsafe void ConvertToDds(ValveTextureFile vtf, int mipMap, int frame, int face, int zslice, Stream outStream) { var oneMipMap = mipMap > -1; if (mipMap < 0) { mipMap = 0; } if (mipMap >= vtf.Header.MipMapCount) { ConvertToDds(vtf, vtf.Header.MipMapCount - 1, frame, face, zslice, outStream); return; } var header = new DdsHeader(); int blockSize; uint fourCC; switch (vtf.Header.HiResFormat) { case TextureFormat.DXT1: blockSize = 8; fourCC = 0x31545844; break; case TextureFormat.DXT5: blockSize = 16; fourCC = 0x35545844; break; default: throw new NotImplementedException(); } GetMipMapSize(vtf.Header.Width, vtf.Header.Height, mipMap, out header.dwWidth, out header.dwHeight); header.dwSize = (uint)Marshal.SizeOf(typeof(DdsHeader)); header.dwFlags = DdsHeaderFlags.CAPS | DdsHeaderFlags.HEIGHT | DdsHeaderFlags.WIDTH | DdsHeaderFlags.PIXELFORMAT | (oneMipMap ? 0 : DdsHeaderFlags.MIPMAPCOUNT); header.dwPitchOrLinearSize = (uint)(Math.Max(1, (vtf.Header.Width + 3) / 4) * blockSize); header.dwDepth = 1; header.dwMipMapCount = oneMipMap ? 1 : (uint)vtf.Header.MipMapCount; header.dwCaps = DdsCaps.TEXTURE | (oneMipMap ? 0 : DdsCaps.MIPMAP); header.ddspf.dwSize = (uint)Marshal.SizeOf(typeof(DdsPixelFormat)); header.ddspf.dwFlags = DdsPixelFormatFlags.FOURCC; header.ddspf.dwFourCC = fourCC; if (_sHeaderBuffer == null) { _sHeaderBuffer = new byte[header.dwSize]; fixed(byte *bufferPtr = _sHeaderBuffer) { var headerPtr = (DdsHeader *)bufferPtr; *headerPtr = header; } using (var writer = new BinaryWriter(outStream, Encoding.ASCII, true)) { writer.Write((uint)0x20534444); writer.Write(_sHeaderBuffer); var endMipMap = oneMipMap ? mipMap + 1 : vtf.Header.MipMapCount; for (var i = mipMap; i < endMipMap; ++i) { vtf.WriteHiResPixelData(i, frame, face, zslice, writer); } } }
public static Texture ToTexture(ValveTextureFile VTF, int Mip = 0, int Frame = 0, int Face = 0, int ZSlice = 0) { int DataLen = VTF.GetHiResPixelDataLength(Mip); int TotalLen = DataLen + 128; byte[] PixelBuffer = null; if (PixelBuffer == null || PixelBuffer.Length < TotalLen) { int PowerOfTwo = 256; while (PowerOfTwo < TotalLen) { PowerOfTwo <<= 1; } PixelBuffer = new byte[PowerOfTwo]; } bool RemoveAlpha = false; Image Img = null; int Offset = 0; int Width = Math.Max(1, VTF.Header.Width >> Mip); int Height = Math.Max(1, VTF.Header.Height >> Mip); MagickReadSettings ReadSettings = new MagickReadSettings { Width = Width, Height = Height }; switch (VTF.Header.HiResFormat) { case TextureFormat.DXT1: case TextureFormat.DXT3: case TextureFormat.DXT5: { if (VTF.Header.HiResFormat == TextureFormat.DXT1) { RemoveAlpha = true; } ReadSettings.Format = MagickFormat.Dds; Offset = DDS.WriteDdsHeader(VTF, Mip, PixelBuffer); break; } case TextureFormat.I8: ReadSettings.Format = MagickFormat.Gray; RemoveAlpha = true; break; case TextureFormat.IA88: ReadSettings.Format = MagickFormat.Graya; break; case TextureFormat.BGRA8888: ReadSettings.Format = MagickFormat.Bgra; break; case TextureFormat.BGR888: ReadSettings.Format = MagickFormat.Bgr; RemoveAlpha = true; break; default: throw new NotImplementedException(); } if (Img == null) { VTF.GetHiResPixelData(Mip, Frame, Face, ZSlice, PixelBuffer, Offset); MagickImage MImg = new MagickImage(PixelBuffer, ReadSettings); if (MImg.Width != Width || MImg.Height != Height) { MImg.Resize(new MagickGeometry(Width, Height) { IgnoreAspectRatio = true }); } Bitmap Bmp = MImg.ToBitmap(); if (RemoveAlpha) { Bmp.RemoveAlpha(); } Img = Bmp; } TextureWrap WrapU = TextureWrap.Repeat; TextureWrap WrapV = TextureWrap.Repeat; if ((VTF.Header.Flags & TextureFlags.CLAMPS) != 0) { WrapU = TextureWrap.ClampToEdge; } if ((VTF.Header.Flags & TextureFlags.CLAMPT) != 0) { WrapV = TextureWrap.ClampToEdge; } if ((VTF.Header.Flags & TextureFlags.CLAMPS) != 0) { WrapU = WrapV = TextureWrap.ClampToBorder; } TextureFilter MinFilter = TextureFilter.LinearMipmapLinear; if ((VTF.Header.Flags & TextureFlags.POINTSAMPLE) != 0) { MinFilter = TextureFilter.NearestMipmapNearest; } Texture T = Texture.FromImage(Img, MipmapLevels: 4); T.SetWrap(WrapU, WrapV); T.SetFilter(MinFilter, TextureFilter.Nearest); return(T); }
public static MagickImage DecodeImage(ValveTextureFile vtf, int mip, int frame, int face, int zslice) { var dataLength = vtf.GetHiResPixelDataLength(mip); // Let's pretend 16bpp formats are really 24bpp switch (vtf.Header.HiResFormat) { case TextureFormat.RGB565: case TextureFormat.BGR565: dataLength = dataLength * 3 / 2; break; } var totalLength = dataLength + 128; if (_sPixelBuffer == null || _sPixelBuffer.Length < totalLength) { var powerOf2 = 256; while (powerOf2 < totalLength) { powerOf2 <<= 1; } _sPixelBuffer = new byte[powerOf2]; } var offset = 0; var width = Math.Max(1, vtf.Header.Width >> mip); var height = Math.Max(1, vtf.Header.Height >> mip); var readSettings = new MagickReadSettings { Width = width, Height = height }; switch (vtf.Header.HiResFormat) { case TextureFormat.DXT1: case TextureFormat.DXT3: case TextureFormat.DXT5: readSettings.Format = MagickFormat.Dds; offset = WriteDdsHeader(vtf, mip, _sPixelBuffer); break; case TextureFormat.I8: readSettings.Format = MagickFormat.Gray; break; case TextureFormat.IA88: readSettings.Format = MagickFormat.Gray; readSettings.PixelStorage = new PixelStorageSettings { StorageType = StorageType.Char, Mapping = "PA" }; break; case TextureFormat.BGR565: case TextureFormat.BGR888: readSettings.PixelStorage = new PixelStorageSettings(StorageType.Char, "BGR"); break; case TextureFormat.RGB565: case TextureFormat.RGB888: case TextureFormat.RGB888_BLUESCREEN: readSettings.PixelStorage = new PixelStorageSettings(StorageType.Char, "RGB"); break; case TextureFormat.ABGR8888: readSettings.PixelStorage = new PixelStorageSettings(StorageType.Char, "ABGR"); break; case TextureFormat.BGRA8888: readSettings.PixelStorage = new PixelStorageSettings(StorageType.Char, "BGRA"); break; case TextureFormat.RGBA8888: readSettings.PixelStorage = new PixelStorageSettings(StorageType.Char, "RGBA"); break; default: throw new NotImplementedException(); } vtf.GetHiResPixelData(mip, frame, face, zslice, _sPixelBuffer, offset); // Convert 16bpp to 24bpp switch (vtf.Header.HiResFormat) { case TextureFormat.RGB565: case TextureFormat.BGR565: for (var i = width * height - 1; i >= 0; --i) { var pixel = (ushort)(_sPixelBuffer[i * 2] | (_sPixelBuffer[i * 2 + 1] << 8)); _sPixelBuffer[i * 3] = (byte)((pixel & 31) / 31f * 255f); _sPixelBuffer[i * 3 + 1] = (byte)(((pixel >> 5) & 63) / 63f * 255f); _sPixelBuffer[i * 3 + 2] = (byte)(((pixel >> 11) & 31) / 31f * 255f); } break; } var img = new MagickImage(_sPixelBuffer, readSettings); if (img.Width != width || img.Height != height) { img.Resize(new MagickGeometry(width, height) { IgnoreAspectRatio = true }); } return(img); }
public static Texture Get(ValveBspFile bsp, string path) { var inBsp = bsp != null && bsp.PakFile.ContainsFile(path); var res = inBsp ? bsp.PakFile : Program.Resources; if (!res.ContainsFile(path)) { return(null); } ValveTextureFile vtf; using (var stream = res.OpenFile(path)) { vtf = new ValveTextureFile(stream); } var isCube = vtf.FaceCount == 6; var untextured = Program.BaseOptions.Untextured && !path.StartsWith("materials/skybox/"); var tex = new Texture { Path = path.ToLower(), Target = isCube ? TextureTarget.TextureCubeMap : TextureTarget.Texture2D, Width = untextured ? 1 : vtf.Header.Width, Height = untextured ? 1 : vtf.Header.Height, FrameCount = vtf.Header.Frames, Params = { Filter = untextured || (vtf.Header.Flags & TextureFlags.POINTSAMPLE) != 0 ? TextureMinFilter.Nearest : TextureMinFilter.Linear, MipMap = !untextured && (vtf.Header.Flags & TextureFlags.NOMIP) == 0, WrapS = (vtf.Header.Flags & TextureFlags.CLAMPS) != 0 ? TextureWrapMode.ClampToEdge : TextureWrapMode.Repeat, WrapT = (vtf.Header.Flags & TextureFlags.CLAMPT) != 0 ? TextureWrapMode.ClampToEdge : TextureWrapMode.Repeat } }; byte[] pixelBuf = null; for (var mip = vtf.MipmapCount - 1; mip >= 0; --mip) { var isSinglePixel = Math.Max(tex.Width >> mip, 1) * Math.Max(tex.Height >> mip, 1) == 1; for (var frame = 0; frame < vtf.FrameCount; ++frame) { for (var face = 0; face < vtf.FaceCount; ++face) { var elem = new TextureElement { Level = untextured ? 0 : mip, Frame = vtf.FrameCount > 1 ? (int?)frame : null }; if (isCube) { elem.Target = TextureTarget.TextureCubeMapPositiveX + face; } if (isSinglePixel) { if (pixelBuf == null) { pixelBuf = new byte[vtf.GetHiResPixelDataLength(mip)]; } vtf.GetHiResPixelData(mip, frame, face, 0, pixelBuf); using (var img = DecodeImage(vtf, mip, frame, face, 0)) { var pixel = img.GetPixels()[0, 0]; switch (pixel.Channels) { case 3: elem.Color = new MaterialColor(pixel[0], pixel[1], pixel[2]); break; case 4: elem.Color = new MaterialColor(pixel[0], pixel[1], pixel[2], pixel[3]); break; default: throw new NotImplementedException(); } } } else { var fileName = $"{path}/mip{mip}"; if (vtf.FrameCount > 1) { fileName = $"{fileName}.frame{frame}"; } if (vtf.FaceCount > 1) { fileName = $"{fileName}.face{face}"; } fileName = $"{fileName}.png"; elem.Url = inBsp ? $"/maps/{bsp.Name}/{fileName}" : $"/{fileName}"; } tex.Elements.Add(elem); } } if (untextured) { break; } } return(tex); }
public void GetImage([Url] string map) { if (Skip) { Response.OutputStream.Close(); return; } var absolute = Request.Url.AbsolutePath; var pathStart = absolute.IndexOf("/materials") + 1; var pathEnd = absolute.IndexOf(".vtf", pathStart) + ".vtf".Length; var path = HttpUtility.UrlDecode(absolute.Substring(pathStart, pathEnd - pathStart)); var fileName = Path.GetFileName(absolute); var match = _sFileNameRegex.Match(fileName); if (!match.Success) { throw NotFoundException(); } var mip = 0; var face = 0; var frame = 0; var index = 0; foreach (Capture capture in match.Groups["param"].Captures) { var param = capture.Value.ToLower(); var value = int.Parse(match.Groups["value"].Captures[index++].Value); switch (param) { case "mip": mip = value; break; case "face": face = value; break; case "frame": frame = value; break; } } var bsp = map == null ? null : Program.GetMap(map); var res = bsp == null ? Program.Resources : bsp.PakFile; ValveTextureFile vtf; using (var stream = res.OpenFile(path)) { vtf = new ValveTextureFile(stream); } var buffer = new byte[vtf.GetHiResPixelDataLength(mip)]; vtf.GetHiResPixelData(mip, frame, face, 0, buffer); using (var image = Texture.DecodeImage(vtf, mip, frame, face, 0)) { image.Write(Response.OutputStream, MagickFormat.Png); } Response.OutputStream.Close(); }