static void WriteImages(BitmapTag bitmap, string outPath) { var ms = new MemoryStream(); for (var i = 0; i < bitmap.TextureInfos[0].LevelsOfDetail.Length; i++) { var lod = bitmap.TextureInfos[0].LevelsOfDetail[i]; if (lod.Data.IsEmpty) { continue; } var ddsHeader = new DdsHeader( bitmap.TextureInfos[0].Format, bitmap.TextureType, bitmap.TextureInfos[0].Width, bitmap.TextureInfos[0].Height, bitmap.TextureInfos[0].Depth, bitmap.MipMapCount, null, null); ddsHeader.HeaderData.CopyTo(ms); ms.Write(lod.Data.ToArray(), 0, lod.Data.Length); } ms.Position = 0; using var image = Pfim.Dds.Create(ms, new Pfim.PfimConfig()); using Image imsImage = image.Format switch { Pfim.ImageFormat.Rgb24 => Image.LoadPixelData <Bgr24>(image.Data, image.Width, image.Height), Pfim.ImageFormat.Rgba32 => Image.LoadPixelData <Bgra32>(image.Data, image.Width, image.Height), _ => throw new NotSupportedException() }; // UI images aren't fully opaque, making it so imsImage.Mutate(x => x.ProcessPixelRowsAsVector4((Span <Vector4> span) => { for (var i = 0; i < span.Length; i++) { if (span[i].W > 0f) { span[i].W = 1f; } } })); imsImage.SaveAsPng(Path.Combine(outPath, "full.png")); imsImage.Mutate(x => x.Resize(ThumbnailSize, ThumbnailSize)); imsImage.SaveAsPng(Path.Combine(outPath, "thumbnail.png")); }
public static void WriteTextureHeader(BitmapTag bitm, Stream destination) { var ddsHeader = DdsHeader.Create( bitm.TextureInfos[0].Format, CapsLookup[bitm.TextureType], Caps2Lookup[bitm.TextureType], bitm.TextureInfos[0].Width, bitm.TextureInfos[0].Height, bitm.TextureInfos[0].Depth, bitm.MipMapCount, null, null); destination.Write(ddsHeader); }
/// <summary> /// Initializes a new <see cref="HaloBitmap"/> instance using the supplied object index entry. /// </summary> /// <param name="entry">The object index entry that contains the bitmap tag group data.</param> /// <exception cref="ArgumentNullException"><paramref name="entry"/> is null.</exception> /// <exception cref="ArgumentException"><paramref name="entry"/> is not a bitmap.</exception> public HaloBitmap(IndexEntry entry) { //Check if (entry == null) { throw new ArgumentNullException(nameof(entry)); } else if (entry.Root != HaloTags.bitm) { throw new ArgumentException("Index entry is not bitmap.", nameof(entry)); } //Setup this.entry = entry; tag = new BitmapTag(entry); maps = new Bitmap[tag.Bitmaps.Length][][]; //Setup Property Accessors bitmaps = new BitmapProperties[tag.Bitmaps.Length]; for (int i = 0; i < tag.Bitmaps.Length; i++) { bitmaps[i] = new BitmapProperties(this, i); } sequences = new SequenceProperties[tag.Sequences.Length]; for (int i = 0; i < tag.Sequences.Length; i++) { sequences[i] = new SequenceProperties(this, i); } //Check if (tag == null) { return; } else if (tag.Bitmaps.Length == 0) { return; } //Loop through bitmaps for (int k = 0; k < tag.Bitmaps.Length; k++) { //Setup BitmapTagGroup.Bitmap bitmap = tag.Bitmaps[k]; maps[k] = new Bitmap[6][]; //Loop through LODs byte[] sourceData = null; for (int l = 0; l < 6; l++) { //Get source data if (bitmap.rawOffsets[l] != uint.MaxValue) { RawLocation rawLocation = (RawLocation)(bitmap.rawOffsets[l] & 0xC0000000); if (rawLocation == RawLocation.Local) { sourceData = entry.Raws[RawSection.Bitmap][(int)bitmap.rawOffsets[l]].GetBuffer(); } else { string filelocation = string.Empty; int rawOffset = (int)(bitmap.rawOffsets[l] & (uint)RawLocation.LocalMask); switch (rawLocation) { case RawLocation.Mainmenu: filelocation = HaloSettings.MainmenuPath; break; case RawLocation.Shared: filelocation = HaloSettings.SharedPath; break; case RawLocation.SinglePlayerShared: filelocation = HaloSettings.SingleplayerSharedPath; break; } //Check if (File.Exists(filelocation)) { using (FileStream fs = new FileStream(filelocation, FileMode.Open)) using (BinaryReader mapReader = new BinaryReader(fs)) { fs.Seek(rawOffset, SeekOrigin.Begin); sourceData = mapReader.ReadBytes(bitmap.rawLengths[l]); } } } } //Set if (sourceData.Length == 0) { continue; } //Prepare BitmapFlags flags = (BitmapFlags)bitmap.flags; BitmapFormat format = (BitmapFormat)bitmap.format; PixelFormat bitmapFormat = PixelFormat.Format32bppArgb; int sourceBits = 32; switch (format) { case BitmapFormat.A8: case BitmapFormat.Y8: case BitmapFormat.P8Bump: case BitmapFormat.P8: case BitmapFormat.Ay8: sourceBits = 8; break; case BitmapFormat.A8y8: case BitmapFormat.A1r5g5b5: case BitmapFormat.A4r4g4b4: case BitmapFormat.V8u8: case BitmapFormat.G8b8: case BitmapFormat.R5g6b5: sourceBits = 16; break; case BitmapFormat.Dxt1: sourceBits = 4; break; case BitmapFormat.Dxt5: case BitmapFormat.Dxt3: sourceBits = 8; break; case BitmapFormat.Argbfp32: sourceBits = 128; break; } //Handle switch (format) { case BitmapFormat.R5g6b5: bitmapFormat = PixelFormat.Format16bppRgb565; break; case BitmapFormat.A1r5g5b5: bitmapFormat = PixelFormat.Format16bppArgb1555; break; case BitmapFormat.X8r8g8b8: bitmapFormat = PixelFormat.Format32bppRgb; break; case BitmapFormat.P8Bump: bitmapFormat = PixelFormat.Format8bppIndexed; break; case BitmapFormat.P8: bitmapFormat = PixelFormat.Format8bppIndexed; break; } //Prepare int width = bitmap.width, height = bitmap.height; if (flags.HasFlag(BitmapFlags.Linear)) { width = (int)Math.Ceiling(width / 16f) * 16; } //Loop LOD for (int i = 0; i < l; i++) { width /= 2; height /= 2; } //Prepare int mapWidth = width, mapHeight = height, location = 0; int mipmapCount = bitmap.mipmapCount; maps[k][l] = new Bitmap[mipmapCount + 1]; Size bitmapSize = Size.Empty; byte[] mapData = null; //Loop for (int i = 1; i <= mipmapCount + 1; i++) { //Prepare int mapIndex = i - 1; mapWidth = width; mapHeight = height; for (int j = 1; j < i; j++) { mapWidth /= 2; mapHeight /= 2; } //Get Size bitmapSize = new Size(mapWidth, mapHeight); //Check if (bitmapSize.Width == 0 || bitmapSize.Height == 0) { continue; } //Create Map int mapStride = mapWidth * sourceBits / 8; int mapSize = mapStride * mapHeight; //Ehh? switch (format) { case BitmapFormat.Dxt1: mapSize = Math.Max(mapSize, 8); break; case BitmapFormat.Dxt3: case BitmapFormat.Dxt5: mapSize = Math.Max(mapSize, 16); break; case BitmapFormat.P8: case BitmapFormat.P8Bump: mapSize = Math.Max(mapSize, 16); break; default: mapSize = Math.Max(mapSize, 1); break; } mapData = new byte[mapSize]; if (location + mapSize > sourceData.Length) { continue; } Array.Copy(sourceData, location, mapData, 0, mapSize); //Deswizzle? if ((flags & BitmapFlags.Swizzled) == BitmapFlags.Swizzled) { mapData = Swizzler.Swizzle(mapData, mapWidth, mapHeight, bitmap.depth, sourceBits, true); } if (mapData == null) { mapData = new byte[mapSize]; } using (Bitmap map = new Bitmap(mapWidth, mapHeight, bitmapFormat)) { unsafe { //Lock Bits BitmapData data = map.LockBits(new Rectangle(0, 0, mapWidth, mapHeight), ImageLockMode.ReadWrite, bitmapFormat); //Prepare Buffer byte[] bitmapData = new byte[data.Stride * data.Height]; int dataLength = Math.Min(bitmapData.Length, mapSize); //Handle Format... switch (format) { case BitmapFormat.A8: for (int x = 0; x < mapWidth * mapHeight; x++) { bitmapData[x * 4 + 0] = 255; bitmapData[x * 4 + 1] = 255; bitmapData[x * 4 + 2] = 255; bitmapData[x * 4 + 3] = mapData[x]; } break; case BitmapFormat.Y8: for (int x = 0; x < mapWidth * mapHeight; x++) { bitmapData[x * 4 + 0] = mapData[x]; bitmapData[x * 4 + 1] = mapData[x]; bitmapData[x * 4 + 2] = mapData[x]; bitmapData[x * 4 + 3] = 255; } break; case BitmapFormat.Ay8: for (int x = 0; x < mapWidth * mapHeight; x++) { bitmapData[x * 4 + 0] = mapData[x]; bitmapData[x * 4 + 1] = mapData[x]; bitmapData[x * 4 + 2] = mapData[x]; bitmapData[x * 4 + 3] = mapData[x]; } break; case BitmapFormat.A8y8: for (int x = 0; x < mapWidth * mapHeight; x++) { bitmapData[x * 4 + 0] = mapData[x * 2]; bitmapData[x * 4 + 1] = mapData[x * 2]; bitmapData[x * 4 + 2] = mapData[x * 2]; bitmapData[x * 4 + 3] = mapData[x * 2 + 1]; } break; case BitmapFormat.A4r4g4b4: for (int x = 0; x < mapWidth * mapHeight; x++) { bitmapData[x * 4 + 0] = (byte)(mapData[x * 2 + 0] & 0xF0); bitmapData[x * 4 + 1] = (byte)(mapData[x * 2 + 0] & 0x0F); bitmapData[x * 4 + 2] = (byte)(mapData[x * 2 + 1] & 0xF0); bitmapData[x * 4 + 3] = (byte)(mapData[x * 2 + 1] & 0x0F); } break; case BitmapFormat.P8Bump: case BitmapFormat.P8: Array.Copy(mapData, 0, bitmapData, 0, dataLength); break; case BitmapFormat.R5g6b5: Array.Copy(mapData, 0, bitmapData, 0, dataLength); break; case BitmapFormat.A1r5g5b5: Array.Copy(mapData, 0, bitmapData, 0, dataLength); break; case BitmapFormat.X8r8g8b8: Array.Copy(mapData, 0, bitmapData, 0, dataLength); break; case BitmapFormat.A8r8g8b8: Array.Copy(mapData, 0, bitmapData, 0, dataLength); break; case BitmapFormat.Dxt1: if (mapWidth >= 4 && mapHeight >= 4) { S3TC.DecompressDxt1(ref bitmapData, mapData, bitmapSize); } break; case BitmapFormat.Dxt3: if (mapWidth >= 4 && mapHeight >= 4) { S3TC.DecompressDxt3(ref bitmapData, mapData, bitmapSize); } break; case BitmapFormat.Dxt5: if (mapWidth >= 4 && mapHeight >= 4) { S3TC.DecompressDxt5(ref bitmapData, mapData, bitmapSize); } break; case BitmapFormat.Argbfp32: for (int x = 0; x < mapWidth * mapHeight; x++) { bitmapData[x * 4 + 0] = (byte)Math.Min((BitConverter.ToSingle(mapData, x * 16 + 8) * 255f), 255f); bitmapData[x * 4 + 1] = (byte)Math.Min((BitConverter.ToSingle(mapData, x * 16 + 4) * 255f), 255f); bitmapData[x * 4 + 2] = (byte)Math.Min((BitConverter.ToSingle(mapData, x * 16) * 255f), 255f); bitmapData[x * 4 + 3] = (byte)Math.Min((BitConverter.ToSingle(mapData, x * 16 + 12) * 255f), 255f); } break; case BitmapFormat.Rgbfp32: break; case BitmapFormat.Rgbfp16: break; case BitmapFormat.V8u8: for (int x = 0; x < mapWidth * mapHeight; x++) { bitmapData[x * 4 + 0] = 255; bitmapData[x * 4 + 1] = (byte)(127 + (sbyte)mapData[x * 2 + 1]); bitmapData[x * 4 + 2] = (byte)(127 + (sbyte)mapData[x * 2]); bitmapData[x * 4 + 3] = 255; } break; case BitmapFormat.G8b8: break; } //Copy Marshal.Copy(bitmapData, 0, data.Scan0, bitmapData.Length); map.UnlockBits(data); //Setup Palettes if (format == BitmapFormat.P8Bump) { map.SetNormalMapPalette(); } else if (format == BitmapFormat.P8) { map.SetGrayscalePalette(); } //Set location += mapSize; } //Draw into cropped image maps[k][l][mapIndex] = new Bitmap(bitmap.width, bitmap.height, bitmapFormat); using (Graphics g = Graphics.FromImage(maps[k][l][mapIndex])) g.DrawImage(map, Point.Empty); } } } } }
private static TagPreviewViewModel GetBitmPreview(BitmapTag bitm) { var preview = new TagPreviewViewModel(); preview.AddItem("bitmap", bitm, GetBitmapPreview); return(preview); object GetBitmapPreview(BitmapTag bitm) { // HACK: hard coding texture 0, needs texture binder rewrite to support here if (bitm.TextureInfos[0].Width == 0 || bitm.TextureInfos[0].Height == 0) { return(null); } var textureBinder = new OpenGLTextureBinder(); var settings = new GameWindowSettings(); var nsettings = new NativeWindowSettings() { API = ContextAPI.OpenGL, AutoLoadBindings = true, Size = new OpenTK.Mathematics.Vector2i(bitm.TextureInfos[0].Width, bitm.TextureInfos[0].Height), Title = "OpenH2", Flags = ContextFlags.Debug | ContextFlags.Offscreen, APIVersion = new Version(4, 0) }; var window = new GameWindow(settings, nsettings); window.IsVisible = false; window.MakeCurrent(); GL.Enable(EnableCap.DebugOutput); GL.DebugMessageCallback(callback, (IntPtr.Zero)); GL.Enable(EnableCap.DepthTest); GL.Enable(EnableCap.CullFace); GL.Enable(EnableCap.Blend); GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); var meshId = UploadQuadMesh(); var matriciesUniform = new GlobalUniform() { ProjectionMatrix = Matrix4x4.CreateOrthographic(2f, 2f, 0, 10), ViewMatrix = Matrix4x4.Identity, ViewPosition = Vector3.Zero }; var shader = ShaderCompiler.CreateShader(Shader.TextureViewer); var handle = textureBinder.GetOrBind(bitm, out var _); GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, handle); GL.ClearColor(0f, 0f, 0f, 1f); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.UseProgram(shader); GL.GenBuffers(1, out uint MatriciesUniformHandle); GL.BindBuffer(BufferTarget.UniformBuffer, MatriciesUniformHandle); GL.BufferData(BufferTarget.UniformBuffer, GlobalUniform.Size, ref matriciesUniform, BufferUsageHint.DynamicDraw); GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, MatriciesUniformHandle); GL.BindBuffer(BufferTarget.UniformBuffer, 0); GL.BindVertexArray(meshId); GL.DrawElements(PrimitiveType.Triangles, 6, DrawElementsType.UnsignedInt, 0); GL.Flush(); var bmp = new WriteableBitmap(new PixelSize(bitm.TextureInfos[0].Width, bitm.TextureInfos[0].Height), new Avalonia.Vector(72, 72), Avalonia.Platform.PixelFormat.Rgba8888); using (var buf = bmp.Lock()) { GL.ReadPixels(0, 0, bitm.TextureInfos[0].Width, bitm.TextureInfos[0].Height, PixelFormat.Rgba, PixelType.UnsignedByte, buf.Address); } window.Close(); window.Dispose(); return(bmp); } }
public override Bitmap DecodeStream(Stream s, ResizeSettings settings, string optionalPath) { if (string.IsNullOrEmpty(optionalPath)) { if (s.CanSeek) { //Check the header instead if no filename is present. byte[] header = new byte[4]; s.Read(header, 0, 4); bool isPdf = (header[0] == '%' && header[1] == 'P' && header[2] == 'D' && header[3] == 'F'); s.Seek(-4, SeekOrigin.Current); //Restore position. if (!isPdf) { return(null); } } else { return(null); //It's not seekable, we can't check the header. } } else if (!_supportedExtensions.Contains(Path.GetExtension(optionalPath), StringComparer.OrdinalIgnoreCase)) { // Not a supported format return(null); } // Do not allow decoding if Ghostscript there are issues with performing this decode IIssue[] issues = GetIssues().ToArray(); if (issues.Length > 0) { string message = string.Join(Environment.NewLine, issues.Select(x => x.Summary).ToArray()); throw new InvalidOperationException(message); } // Must write input stream to a temporary file for Ghostscript to process. FileInfo tempInputPathInfo = new FileInfo(Path.GetTempFileName()); try { using (FileStream tempInputStream = tempInputPathInfo.Create()) { StreamExtensions.CopyToStream(s, tempInputStream); } // Get information about the PDF such as page count and media boxes // Although this creates a second trip to Ghostscript engine, it's not possible to generate a rendered image in exact // dimensions requested. Skipping this step will cause a rendered image, of some size, to be resized further. PdfInfo pdfInfo = GetPdfInfo(tempInputPathInfo.FullName); // Extract the requested page number from resize settings, or default to first page int pageNumber = settings.GetValueOrDefault("page", 1); // Try to get the page number from PDF info. If not available, abort. This is caused by // requesting a page that does not exist. PageInfo pageInfo = pdfInfo.Pages.SingleOrDefault(x => x.Number == pageNumber); if (pageInfo == null) { return(null); } // We only support media box (as opposed to clip, bleed, art, etc.) If this is not available, abort. if (pageInfo.MediaBox == null) { return(null); } // Get the output size of the generated bitmap by applying the resize settings and media box. Size outputSize = (pageInfo.Rotate == -90 || pageInfo.Rotate == 90) ? GetOutputSize(settings, pageInfo.MediaBox.Height, pageInfo.MediaBox.Width) : GetOutputSize(settings, pageInfo.MediaBox.Width, pageInfo.MediaBox.Height); // Create default Ghostscript settings and apply the resize settings. GhostscriptSettings ghostscriptSettings = new GhostscriptSettings { GhostscriptArgument.NoPause, GhostscriptArgument.Quiet, GhostscriptArgument.Safer, GhostscriptArgument.Batch, { GhostscriptArgument.OutputDevice, "pngalpha" }, { GhostscriptArgument.MaxBitmap, 24000000 }, { GhostscriptArgument.RenderingThreads, 4 }, { GhostscriptArgument.GridFitTT, 0 }, { GhostscriptArgument.AlignToPixels, 0 }, //Subpixel rendering depends on output device... perhaps testing would help determine if 1 would be better? { GhostscriptArgument.FirstPage, pageNumber }, { GhostscriptArgument.LastPage, pageNumber }, GhostscriptArgument.Printed, GhostscriptArgument.PdfFitPage, GhostscriptArgument.FixedMedia, { GhostscriptArgument.Height, outputSize.Height }, { GhostscriptArgument.Width, outputSize.Width } }; ApplyResizeSettings(settings, ghostscriptSettings); // Have Ghostscript process the input to a PNG file with transparency. // The PNG will be reloaded and further processed by the resizer pipeline. FileInfo tempOutputPathInfo = new FileInfo(Path.GetTempFileName()); try { // Add output file and input file. The input file must be the very last argument. ghostscriptSettings.Add(GhostscriptArgument.OutputFile, tempOutputPathInfo.FullName); ghostscriptSettings.Add(tempInputPathInfo.FullName); _engine.Execute(ghostscriptSettings); // NOTE: Do not dispose of memory stream because it is used as the backing source for the loaded bitmap. MemoryStream memoryStream = new MemoryStream((int)tempOutputPathInfo.Length); using (FileStream fileStream = tempOutputPathInfo.Open(FileMode.Open)) { StreamExtensions.CopyToStream(fileStream, memoryStream); } // Per ImagerResizer plugin example source code: // NOTE: If the Bitmap class is used for decoding, read gdi-bugs.txt and make sure you set b.Tag to new BitmapTag(optionalPath,stream); BitmapTag bitmapTag = new BitmapTag("ghostscript.png", memoryStream); return(new Bitmap(memoryStream) { Tag = bitmapTag }); } //catch(GhostscriptException) //{ // // Conversion failed // return null; //or maybe we should show details? If it's a valid PDF? //} finally { tempOutputPathInfo.Delete(); } } finally { tempInputPathInfo.Delete(); } }