using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Runtime.InteropServices; using Decal.Adapter.Support; using Decal.Interop.Core; using Decal.Interop.Render; namespace Decal.Adapter.Wrappers; [CLSCompliant(true)] public class HudRenderTarget : MarshalByRefObject, IDisposable { private IRenderTarget myTarget; private bool isDisposed; private Rectangle areaRect; private bool inRender; private bool inText; public bool Lost => myTarget.Lost; [EditorBrowsable(EditorBrowsableState.Never)] public Rectangle Constraint { get { Rectangle region = Region; return new Rectangle(0, 0, region.Width, region.Height); } } public Rectangle Region { get { return Util.toRectangle(myTarget.Region); } set { tagRECT pVal = Util.toTagRECT(value); myTarget.Region = ref pVal; areaRect.Width = value.Width; areaRect.Height = value.Height; } } [EditorBrowsable(EditorBrowsableState.Never)] public object UnsafeSurface => myTarget.GetSurface(); [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] public tagRECT UnsafeRegion { get { return myTarget.Region; } set { myTarget.Region = ref value; } } protected bool IsDisposed => isDisposed; [CLSCompliant(false)] protected HudRenderTarget(IRenderTarget newTarget) { if (newTarget == null) { throw new ArgumentNullException("newTarget"); } myTarget = newTarget; tagRECT region = myTarget.Region; areaRect = new Rectangle(0, 0, region.right - region.left, region.bottom - region.top); } ~HudRenderTarget() { Dispose(disposing: false); } /// /// Begin rendering to this object, must be paired with EndRender /// public void BeginRender() { BeginRender(enableTextureFilter: false); } /// /// Begin rendering to this object, must be paired with EndRender /// /// True to enable texture filtering during rendering public void BeginRender(bool enableTextureFilter) { if (!inRender) { myTarget.BeginRender(enableTextureFilter); inRender = true; return; } throw new RenderViolationException("Cannot call 'BeginRender' when already rendering"); } /// /// End rendering to this object, must be paired with BeginRender /// public void EndRender() { if (inRender) { myTarget.EndRender(); inRender = false; return; } throw new RenderViolationException("Cannot call 'EndRender' without a previous call to 'BeginRender'"); } /// /// Begin writing text /// /// Name of the font to use for writing text /// Height in pixels to use when writing text public void BeginText(string fontName, int pixelHeight) { BeginText(fontName, pixelHeight, FontWeight.Normal, italic: false); } /// /// Begin writing text /// /// Name of the font to use for writing text /// Height in pixels to use when writing text /// Bold, Strong, etc, indicator for text to be written /// Write text in italics? public void BeginText(string fontName, int pixelHeight, FontWeight weight, bool italic) { UnsafeBeginText(fontName, pixelHeight, (int)weight, italic); } /// /// Display text,using the full rectangle of the render target as the area. /// This overload will draw the text in black. /// /// Text to display public void WriteText(string text) { WriteText(text, Color.Black, WriteTextFormats.None, areaRect); } /// /// Display text,using the full rectangle of the render target as the area. /// /// Text to display /// Color to use to display the text. (Including alpha channel) public void WriteText(string text, Color color) { WriteText(text, color, WriteTextFormats.None, areaRect); } /// /// Display text,using the full rectangle of the render target as the area. /// /// Text to display /// Color to use to display the text. (Including alpha channel) /// Format specifiers, including centered, etc. /// Rectangle relative to this object to use to bound the text public void WriteText(string text, Color color, WriteTextFormats format, Rectangle region) { UnsafeWriteText(Util.toTagRECT(region), color.ToArgb(), (int)format, text); } /// /// Display text,using the full rectangle of the render target as the area. /// /// Text to display /// Color to use to display the text. (Including alpha channel) /// Format specifiers, including centered, etc. /// Rectangle relative to this object to use to bound the text public void WriteText(string text, int color, WriteTextFormats format, Rectangle region) { UnsafeWriteText(Util.toTagRECT(region), color, (int)format, text); } /// /// End drawing of text. /// public void EndText() { if (inRender && inText) { myTarget.EndText(); inText = false; return; } throw new RenderViolationException("Cannot call 'EndText' without a previous call to 'BeginText'"); } public void Clear() { Clear(areaRect); } public void Clear(Rectangle clearArea) { UnsafeClear(Util.toTagRECT(clearArea)); } public void DrawPortalImage(int portalFile, Rectangle destinationArea) { UnsafeDrawPortalImage(portalFile, Util.toTagRECT(destinationArea)); } public void DrawPortalImage(int portalFile, int alpha, Rectangle srcArea, Rectangle destinationArea) { UnsafeDrawPortalImage(portalFile, alpha, Util.toTagRECT(srcArea), Util.toTagRECT(destinationArea)); } public void DrawPortalImage(int portalFile, Rectangle srcArea, Rectangle destinationArea) { UnsafeDrawPortalImage(portalFile, 255, Util.toTagRECT(srcArea), Util.toTagRECT(destinationArea)); } public void TilePortalImage(int portalFile, Rectangle destinationArea) { UnsafeTilePortalImage(portalFile, Util.toTagRECT(destinationArea)); } public void TilePortalImage(int portalFile, Rectangle srcArea, Rectangle destinationArea) { UnsafeTilePortalImage(portalFile, Util.toTagRECT(srcArea), Util.toTagRECT(destinationArea)); } public void DrawImage(Image image, Rectangle destinationRegion) { DrawImage(image, destinationRegion, Color.Cyan.ToArgb()); } public void DrawImage(Image image, Rectangle destinationRegion, Color colorKey) { DrawImage(image, destinationRegion, colorKey.ToArgb()); } public void DrawImage(Image image, Rectangle destinationRegion, int colorKey) { if (image == null) { throw new ArgumentNullException("image"); } MemoryStream memoryStream = new MemoryStream(); image.Save(memoryStream, ImageFormat.Bmp); byte[] buffer = memoryStream.GetBuffer(); IntPtr zero = IntPtr.Zero; int num = buffer.Length; zero = Marshal.AllocCoTaskMem(num); try { Marshal.Copy(buffer, 0, zero, num); if (zero != IntPtr.Zero) { UnsafeDrawImage(zero, num, Util.toTagRECT(destinationRegion), colorKey); } } finally { Marshal.FreeCoTaskMem(zero); memoryStream.Dispose(); } } public void Fill(Color color) { Fill(areaRect, color); } public void Fill(Rectangle fillArea, Color color) { Fill(fillArea, color.ToArgb()); } public void Fill(Rectangle fillArea, int color) { UnsafeFill(Util.toTagRECT(fillArea), color); } [EditorBrowsable(EditorBrowsableState.Never)] public void UnsafeBeginText(string fontName, int pixelHeight, int weight, bool italic) { if (inRender && !inText) { myTarget.BeginText(fontName, pixelHeight, weight, italic); inText = true; return; } if (inRender && inText) { throw new RenderViolationException("Cannot call 'BeginText' when already rendering text"); } throw new RenderViolationException("Cannot call 'BeginText' outside of a BeginRender/EndRender block"); } [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] public void UnsafeWriteText(tagRECT region, int color, int format, string text) { if (inRender && inText) { myTarget.WriteText(ref region, color, format, text); return; } throw new RenderViolationException("Cannot call 'WriteText' outside of a BeginText/EndText block"); } [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] public void UnsafeClear(tagRECT clearArea) { if (!inRender) { myTarget.Clear(ref clearArea); return; } throw new RenderViolationException("MUST call 'Clear' outside of a BeginRender/EndRender block"); } [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] public void UnsafeDrawPortalImage(int portalFile, tagRECT destinationArea) { if (inRender) { myTarget.DrawPortalImage(portalFile, ref destinationArea); return; } throw new RenderViolationException("Cannot call 'DrawPortalImage' outside of a BeginRender/EndRender block"); } [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] public void UnsafeDrawPortalImage(int portalFile, int alpha, tagRECT srcArea, tagRECT destinationArea) { if (inRender) { myTarget.DrawPortalImageEx(portalFile, alpha, ref srcArea, ref destinationArea); return; } throw new RenderViolationException("Cannot call 'DrawPortalImageEx' outside of a BeginRender/EndRender block"); } [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] public void UnsafeTilePortalImage(int portalFile, tagRECT destinationArea) { if (inRender) { myTarget.TilePortalImage(portalFile, ref destinationArea); return; } throw new RenderViolationException("Cannot call 'TilePortalImage' outside of a BeginRender/EndRender block"); } [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] public void UnsafeTilePortalImage(int portalFile, tagRECT srcArea, tagRECT destinationArea) { if (inRender) { myTarget.TilePortalImageEx(portalFile, ref srcArea, ref destinationArea); return; } throw new RenderViolationException("Cannot call 'TilePortalImageEx' outside of a BeginRender/EndRender block"); } [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] public void UnsafeDrawImage(IntPtr buffer, int size, tagRECT destinationRegion, int colorKey) { if (inRender) { myTarget.DrawImage((int)buffer, size, ref destinationRegion, colorKey); return; } throw new RenderViolationException("Cannot call 'DrawImage' outside of a BeginRender/EndRender block"); } [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] public void UnsafeFill(tagRECT fillArea, int color) { if (!inRender) { myTarget.Fill(ref fillArea, color); return; } throw new RenderViolationException("MUST call 'Fill' outside of a BeginRender/EndRender block"); } [EditorBrowsable(EditorBrowsableState.Never)] public void UnsafeSetSurface(object pSurface) { myTarget.SetSurface(pSurface); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!isDisposed) { } if (myTarget != null) { Marshal.ReleaseComObject(myTarget); } myTarget = null; isDisposed = true; } }