emergency measures to return display thread stuff to the way it was before (i think? i hope?) as a reasonable baseline when no video pipeline fanciness is called for (no lua overlay, for instance) so that this is useful for gaming again. also add a toggle for the entire HUD system which bypasses some extra work for people stuck in sysdrawing mode who need more speed.
This commit is contained in:
parent
966509858d
commit
5b86a9c375
|
@ -196,6 +196,7 @@ namespace BizHawk.MultiClient
|
|||
public int DispMultiy = 0;
|
||||
public int DispMultianchor = 0;
|
||||
public bool DisplayGDI = false;
|
||||
public bool SuppressGui = false;
|
||||
public bool DisplayStatusBar = true;
|
||||
public int DispRamWatchx = 0;
|
||||
public int DispRamWatchy = 70;
|
||||
|
|
|
@ -8,8 +8,79 @@ using System.Drawing;
|
|||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
//using dx=SlimDX;
|
||||
//using d3d=SlimDX.Direct3D9;
|
||||
|
||||
namespace BizHawk.MultiClient
|
||||
{
|
||||
/// <summary>
|
||||
/// encapsulates thread-safe concept of pending/current display surfaces, reusing buffers where matching
|
||||
/// sizes are available and keeping them cleaned up when they dont seem like theyll need to be used anymore
|
||||
/// </summary>
|
||||
class SwappableDisplaySurfaceSet
|
||||
{
|
||||
DisplaySurface Pending, Current;
|
||||
Queue<DisplaySurface> ReleasedSurfaces = new Queue<DisplaySurface>();
|
||||
|
||||
/// <summary>
|
||||
/// retrieves a surface with the specified size, reusing an old buffer if available and clearing if requested
|
||||
/// </summary>
|
||||
public DisplaySurface AllocateSurface(int width, int height, bool needsClear = true)
|
||||
{
|
||||
for (; ; )
|
||||
{
|
||||
DisplaySurface trial;
|
||||
lock (this)
|
||||
{
|
||||
if (ReleasedSurfaces.Count == 0) break;
|
||||
trial = ReleasedSurfaces.Dequeue();
|
||||
}
|
||||
if (trial.Width == width && trial.Height == height)
|
||||
{
|
||||
if (needsClear) trial.Clear();
|
||||
return trial;
|
||||
}
|
||||
trial.Dispose();
|
||||
}
|
||||
return new DisplaySurface(width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sets the provided buffer as pending. takes control of the supplied buffer
|
||||
/// </summary>
|
||||
public void SetPending(DisplaySurface newPending)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (Pending != null) ReleasedSurfaces.Enqueue(Pending);
|
||||
Pending = newPending;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseSurface(DisplaySurface surface)
|
||||
{
|
||||
lock (this) ReleasedSurfaces.Enqueue(surface);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns the current buffer, making the most recent pending buffer (if there is such) as the new current first.
|
||||
/// </summary>
|
||||
public DisplaySurface GetCurrent()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (Pending != null)
|
||||
{
|
||||
if (Current != null) ReleasedSurfaces.Enqueue(Current);
|
||||
Current = Pending;
|
||||
Pending = null;
|
||||
}
|
||||
}
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface IDisplayFilter
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -29,6 +100,15 @@ namespace BizHawk.MultiClient
|
|||
public Size OutputSize;
|
||||
}
|
||||
|
||||
interface IDisplayDriver
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class Direct3DDisplayDriver : IDisplayDriver
|
||||
{
|
||||
}
|
||||
|
||||
public class DisplaySurface : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -64,6 +144,19 @@ namespace BizHawk.MultiClient
|
|||
return bmp;
|
||||
}
|
||||
|
||||
public static DisplaySurface DisplaySurfaceWrappingBitmap(Bitmap bmp)
|
||||
{
|
||||
DisplaySurface ret = new DisplaySurface();
|
||||
ret.Width = bmp.Width;
|
||||
ret.Height = bmp.Height;
|
||||
ret.bmp = bmp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private DisplaySurface()
|
||||
{
|
||||
}
|
||||
|
||||
public DisplaySurface(int width, int height)
|
||||
{
|
||||
//can't create a bitmap with zero dimensions, so for now, just bump it up to one
|
||||
|
@ -517,72 +610,6 @@ namespace BizHawk.MultiClient
|
|||
//the surface to use to render a lua layer at native resolution (under the OSD)
|
||||
DisplaySurface luaNativeSurfacePreOSD;
|
||||
|
||||
/// <summary>
|
||||
/// encapsulates thread-safe concept of pending/current display surfaces, reusing buffers where matching
|
||||
/// sizes are available and keeping them cleaned up when they dont seem like theyll need to be used anymore
|
||||
/// </summary>
|
||||
class SwappableDisplaySurfaceSet
|
||||
{
|
||||
DisplaySurface Pending, Current;
|
||||
Queue<DisplaySurface> ReleasedSurfaces = new Queue<DisplaySurface>();
|
||||
|
||||
/// <summary>
|
||||
/// retrieves a surface with the specified size, reusing an old buffer if available and clearing if requested
|
||||
/// </summary>
|
||||
public DisplaySurface AllocateSurface(int width, int height, bool needsClear=true)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
DisplaySurface trial;
|
||||
lock (this)
|
||||
{
|
||||
if (ReleasedSurfaces.Count == 0) break;
|
||||
trial = ReleasedSurfaces.Dequeue();
|
||||
}
|
||||
if (trial.Width == width && trial.Height == height)
|
||||
{
|
||||
if(needsClear) trial.Clear();
|
||||
return trial;
|
||||
}
|
||||
trial.Dispose();
|
||||
}
|
||||
return new DisplaySurface(width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sets the provided buffer as pending. takes control of the supplied buffer
|
||||
/// </summary>
|
||||
public void SetPending(DisplaySurface newPending)
|
||||
{
|
||||
lock(this)
|
||||
{
|
||||
if (Pending != null) ReleasedSurfaces.Enqueue(Pending);
|
||||
Pending = newPending;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseSurface(DisplaySurface surface)
|
||||
{
|
||||
lock (this) ReleasedSurfaces.Enqueue(surface);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns the current buffer, making the most recent pending buffer (if there is such) as the new current first.
|
||||
/// </summary>
|
||||
public DisplaySurface GetCurrent()
|
||||
{
|
||||
lock(this)
|
||||
{
|
||||
if(Pending != null)
|
||||
{
|
||||
if (Current != null) ReleasedSurfaces.Enqueue(Current);
|
||||
Current = Pending;
|
||||
Pending = null;
|
||||
}
|
||||
}
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
|
||||
SwappableDisplaySurfaceSet luaNativeSurfaceSet = new SwappableDisplaySurfaceSet();
|
||||
public void SetLuaSurfaceNativePreOSD(DisplaySurface surface) { luaNativeSurfaceSet.SetPending(surface); }
|
||||
|
@ -680,40 +707,76 @@ namespace BizHawk.MultiClient
|
|||
|
||||
int w = currNativeWidth;
|
||||
int h = currNativeHeight;
|
||||
var nativeBmp = nativeDisplaySurfaceSet.AllocateSurface(w, h, true);
|
||||
using (var g = Graphics.FromImage(nativeBmp.PeekBitmap()))
|
||||
|
||||
DisplaySurface luaEmuSurface = luaEmuSurfaceSet.GetCurrent();
|
||||
DisplaySurface luaSurface = luaNativeSurfaceSet.GetCurrent();
|
||||
|
||||
//do we have anything to do?
|
||||
bool complexComposite = false;
|
||||
if (luaEmuSurface != null) complexComposite = true;
|
||||
if (luaSurface != null) complexComposite = true;
|
||||
|
||||
if (!complexComposite)
|
||||
{
|
||||
//scale the source bitmap to the desired size of the render panel
|
||||
g.PixelOffsetMode = PixelOffsetMode.HighSpeed;
|
||||
g.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||||
g.CompositingMode = CompositingMode.SourceCopy;
|
||||
g.CompositingQuality = CompositingQuality.HighSpeed;
|
||||
g.DrawImage(currentSourceSurface.PeekBitmap(), 0, 0, w, h);
|
||||
if (Global.Config.SuppressGui)
|
||||
{
|
||||
Global.RenderPanel.FastRenderAndPresent(currentSourceSurface);
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.RenderPanel.Render(currentSourceSurface);
|
||||
var nativeBmp = nativeDisplaySurfaceSet.AllocateSurface(w, h, true);
|
||||
using (var g = Graphics.FromImage(nativeBmp.PeekBitmap()))
|
||||
{
|
||||
Global.OSD.DrawScreenInfo(g);
|
||||
Global.OSD.DrawMessages(g);
|
||||
}
|
||||
Global.RenderPanel.RenderOverlay(nativeBmp);
|
||||
//release the native resolution image
|
||||
nativeDisplaySurfaceSet.ReleaseSurface(nativeBmp);
|
||||
Global.RenderPanel.Present();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var nativeBmp = nativeDisplaySurfaceSet.AllocateSurface(w, h, true);
|
||||
using (var g = Graphics.FromImage(nativeBmp.PeekBitmap()))
|
||||
{
|
||||
//scale the source bitmap to the desired size of the render panel
|
||||
g.PixelOffsetMode = PixelOffsetMode.HighSpeed;
|
||||
g.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||||
g.CompositingMode = CompositingMode.SourceCopy;
|
||||
g.CompositingQuality = CompositingQuality.HighSpeed;
|
||||
g.DrawImage(currentSourceSurface.PeekBitmap(), 0, 0, w, h);
|
||||
|
||||
//switch to fancier composition for OSD overlays and such
|
||||
g.CompositingMode = CompositingMode.SourceOver;
|
||||
//switch to fancier composition for OSD overlays and such
|
||||
g.CompositingMode = CompositingMode.SourceOver;
|
||||
|
||||
//this could have been done onto the source surface earlier and then scaled only once but the whole composition system needs revising, soo..
|
||||
DisplaySurface luaEmuSurface = luaEmuSurfaceSet.GetCurrent();
|
||||
if (luaEmuSurface != null) g.DrawImage(luaEmuSurface.PeekBitmap(), 0, 0, w, h);
|
||||
g.Clip = new Region(new Rectangle(0, 0, nativeBmp.Width, nativeBmp.Height));
|
||||
//this could have been done onto the source surface earlier and then scaled only once but the whole composition system needs revising, soo..
|
||||
if (luaEmuSurface != null) g.DrawImage(luaEmuSurface.PeekBitmap(), 0, 0, w, h);
|
||||
g.Clip = new Region(new Rectangle(0, 0, nativeBmp.Width, nativeBmp.Height));
|
||||
|
||||
//apply a lua layer
|
||||
var luaSurface = luaNativeSurfaceSet.GetCurrent();
|
||||
if (luaSurface != null) g.DrawImageUnscaled(luaSurface.PeekBitmap(), 0, 0);
|
||||
//although we may want to change this if we want to fade out messages or have some other fancy alpha faded gui stuff
|
||||
//apply a lua layer
|
||||
if (luaSurface != null) g.DrawImageUnscaled(luaSurface.PeekBitmap(), 0, 0);
|
||||
//although we may want to change this if we want to fade out messages or have some other fancy alpha faded gui stuff
|
||||
|
||||
//draw the OSD at native resolution
|
||||
Global.OSD.DrawScreenInfo(g);
|
||||
Global.OSD.DrawMessages(g);
|
||||
g.Clip.Dispose();
|
||||
//draw the OSD at native resolution
|
||||
if (!Global.Config.SuppressGui)
|
||||
{
|
||||
Global.OSD.DrawScreenInfo(g);
|
||||
Global.OSD.DrawMessages(g);
|
||||
}
|
||||
g.Clip.Dispose();
|
||||
}
|
||||
|
||||
//send the native resolution image to the render panel
|
||||
Global.RenderPanel.Render(nativeBmp);
|
||||
Global.RenderPanel.Present();
|
||||
|
||||
//release the native resolution image
|
||||
nativeDisplaySurfaceSet.ReleaseSurface(nativeBmp);
|
||||
}
|
||||
|
||||
//send the native resolution image to the render panel
|
||||
Global.RenderPanel.Render(nativeBmp);
|
||||
|
||||
//release the native resolution image
|
||||
nativeDisplaySurfaceSet.ReleaseSurface(nativeBmp);
|
||||
}
|
||||
|
||||
Thread displayThread;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -458,10 +458,14 @@ namespace BizHawk.MultiClient
|
|||
private void forceGDIPPresentationToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Global.Config.DisplayGDI ^= true;
|
||||
Global.DisplayManager.Suspend();
|
||||
SyncPresentationMode();
|
||||
}
|
||||
|
||||
private void miSuppressGuiLayer_Click(object sender, EventArgs e)
|
||||
{
|
||||
Global.Config.SuppressGui ^= true;
|
||||
}
|
||||
|
||||
private void debuggerToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
LoadNESDebugger();
|
||||
|
@ -1168,6 +1172,7 @@ namespace BizHawk.MultiClient
|
|||
startPausedToolStripMenuItem.Checked = Global.Config.StartPaused;
|
||||
saveWindowPositionToolStripMenuItem.Checked = Global.Config.SaveWindowPosition;
|
||||
forceGDIPPresentationToolStripMenuItem.Checked = Global.Config.DisplayGDI;
|
||||
miSuppressGuiLayer.Checked = Global.Config.SuppressGui;
|
||||
showMenuInFullScreenToolStripMenuItem.Checked = Global.Config.ShowMenuInFullscreen;
|
||||
runInBackgroundToolStripMenuItem.Checked = Global.Config.RunInBackground;
|
||||
acceptBackgroundInputToolStripMenuItem.Checked = Global.Config.AcceptBackgroundInput;
|
||||
|
|
|
@ -3149,5 +3149,7 @@ namespace BizHawk.MultiClient
|
|||
}
|
||||
FrameBufferResized();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using sysdrawing2d=System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
@ -73,7 +74,7 @@ namespace BizHawk.MultiClient
|
|||
while (textureWidth < imageWidth) textureWidth <<= 1;
|
||||
while (textureHeight < imageHeight) textureHeight <<= 1;
|
||||
// Create a new texture instance.
|
||||
Texture = new Texture(GraphicsDevice, textureWidth, textureHeight, 1, Usage.Dynamic, Format.X8R8G8B8, Pool.Default);
|
||||
Texture = new Texture(GraphicsDevice, textureWidth, textureHeight, 1, Usage.Dynamic, Format.A8R8G8B8, Pool.Default);
|
||||
}
|
||||
|
||||
// Copy the image data to the texture.
|
||||
|
@ -116,9 +117,13 @@ namespace BizHawk.MultiClient
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
public interface IRenderer : IDisposable
|
||||
{
|
||||
void FastRenderAndPresent(DisplaySurface surface);
|
||||
void Render(DisplaySurface surface);
|
||||
void RenderOverlay(DisplaySurface surface);
|
||||
void Present();
|
||||
bool Resized { get; set; }
|
||||
Size NativeSize { get; }
|
||||
}
|
||||
|
@ -129,6 +134,7 @@ namespace BizHawk.MultiClient
|
|||
public void Dispose() { }
|
||||
public void Render(DisplaySurface surface)
|
||||
{
|
||||
backingControl.ReleaseCallback = RetainedViewportPanelDisposeCallback;
|
||||
//Color BackgroundColor = Color.FromArgb(video.BackgroundColor);
|
||||
//int[] data = video.GetVideoBuffer();
|
||||
|
||||
|
@ -140,10 +146,57 @@ namespace BizHawk.MultiClient
|
|||
|
||||
//bmp.UnlockBits(bmpdata);
|
||||
|
||||
Bitmap bmp = (Bitmap)surface.PeekBitmap().Clone();
|
||||
lock(this)
|
||||
tempBuffer = surfaceSet.AllocateSurface(backingControl.Width, backingControl.Height, false);
|
||||
|
||||
backingControl.SetBitmap(bmp);
|
||||
RenderInternal(surface, false);
|
||||
}
|
||||
SwappableDisplaySurfaceSet surfaceSet = new SwappableDisplaySurfaceSet();
|
||||
|
||||
DisplaySurface tempBuffer;
|
||||
|
||||
bool RetainedViewportPanelDisposeCallback(Bitmap bmp)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
DisplaySurface tempSurface = DisplaySurface.DisplaySurfaceWrappingBitmap(bmp);
|
||||
surfaceSet.ReleaseSurface(tempSurface);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RenderInternal(DisplaySurface surface, bool transparent = false)
|
||||
{
|
||||
using (var g = Graphics.FromImage(tempBuffer.PeekBitmap()))
|
||||
{
|
||||
g.PixelOffsetMode = sysdrawing2d.PixelOffsetMode.HighSpeed;
|
||||
g.InterpolationMode = sysdrawing2d.InterpolationMode.NearestNeighbor;
|
||||
if (transparent) g.CompositingMode = sysdrawing2d.CompositingMode.SourceOver;
|
||||
else g.CompositingMode = sysdrawing2d.CompositingMode.SourceCopy;
|
||||
g.CompositingQuality = sysdrawing2d.CompositingQuality.HighSpeed;
|
||||
if (backingControl.Width == surface.Width && backingControl.Height == surface.Height)
|
||||
g.DrawImageUnscaled(surface.PeekBitmap(), 0, 0);
|
||||
else
|
||||
g.DrawImage(surface.PeekBitmap(), 0, 0, backingControl.Width, backingControl.Height);
|
||||
}
|
||||
}
|
||||
|
||||
public void FastRenderAndPresent(DisplaySurface surface)
|
||||
{
|
||||
backingControl.SetBitmap((Bitmap)surface.PeekBitmap().Clone());
|
||||
}
|
||||
|
||||
public void RenderOverlay(DisplaySurface surface)
|
||||
{
|
||||
RenderInternal(surface, true);
|
||||
}
|
||||
|
||||
public void Present()
|
||||
{
|
||||
backingControl.SetBitmap(tempBuffer.PeekBitmap());
|
||||
tempBuffer = null;
|
||||
}
|
||||
|
||||
public SysdrawingRenderPanel(RetainedViewportPanel control)
|
||||
{
|
||||
backingControl = control;
|
||||
|
@ -242,14 +295,30 @@ namespace BizHawk.MultiClient
|
|||
|
||||
Resized = false;
|
||||
Device.Clear(ClearFlags.Target, BackgroundColor, 1.0f, 0);
|
||||
Device.Present(Present.DoNotWait);
|
||||
Device.Present(SlimDX.Direct3D9.Present.DoNotWait);
|
||||
}
|
||||
|
||||
public void FastRenderAndPresent(DisplaySurface surface)
|
||||
{
|
||||
Render(surface);
|
||||
Present();
|
||||
}
|
||||
|
||||
public void RenderOverlay(DisplaySurface surface)
|
||||
{
|
||||
RenderWrapper(() => RenderExec(surface, true));
|
||||
}
|
||||
|
||||
public void Render(DisplaySurface surface)
|
||||
{
|
||||
RenderWrapper(() => RenderExec(surface, false));
|
||||
}
|
||||
|
||||
public void RenderWrapper(Action todo)
|
||||
{
|
||||
try
|
||||
{
|
||||
RenderExec(surface);
|
||||
todo();
|
||||
}
|
||||
catch (Direct3D9Exception)
|
||||
{
|
||||
|
@ -264,11 +333,11 @@ namespace BizHawk.MultiClient
|
|||
// lets try recovery!
|
||||
DestroyDevice();
|
||||
backingControl.Invoke(() => CreateDevice());
|
||||
RenderExec(surface);
|
||||
todo();
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderExec(DisplaySurface surface)
|
||||
private void RenderExec(DisplaySurface surface, bool overlay)
|
||||
{
|
||||
if (surface == null)
|
||||
{
|
||||
|
@ -285,7 +354,7 @@ namespace BizHawk.MultiClient
|
|||
|
||||
Texture.SetImage(surface, surface.Width, surface.Height);
|
||||
|
||||
Device.Clear(ClearFlags.Target, BackgroundColor, 1.0f, 0);
|
||||
if(!overlay) Device.Clear(ClearFlags.Target, BackgroundColor, 0.0f, 0);
|
||||
|
||||
// figure out scaling factor
|
||||
float widthScale = (float)backingControl.Size.Width / surface.Width;
|
||||
|
@ -294,15 +363,22 @@ namespace BizHawk.MultiClient
|
|||
|
||||
Device.BeginScene();
|
||||
|
||||
Sprite.Begin(SpriteFlags.None);
|
||||
SpriteFlags flags = SpriteFlags.None;
|
||||
if (overlay) flags |= SpriteFlags.AlphaBlend;
|
||||
Sprite.Begin(flags);
|
||||
Device.SetSamplerState(0, SamplerState.MagFilter, TextureFilter.Point);
|
||||
Device.SetSamplerState(1, SamplerState.MagFilter, TextureFilter.Point);
|
||||
Sprite.Transform = Matrix.Scaling(finalScale, finalScale, 0f);
|
||||
Sprite.Draw(Texture.Texture, new Rectangle(0, 0, surface.Width, surface.Height), new Vector3(surface.Width / 2f, surface.Height / 2f, 0), new Vector3(backingControl.Size.Width / 2f / finalScale, backingControl.Size.Height / 2f / finalScale, 0), Color.White);
|
||||
if (overlay) Device.SetRenderState(RenderState.AlphaBlendEnable, false);
|
||||
Sprite.End();
|
||||
|
||||
Device.EndScene();
|
||||
Device.Present(Present.DoNotWait);
|
||||
}
|
||||
|
||||
public void Present()
|
||||
{
|
||||
Device.Present(SlimDX.Direct3D9.Present.DoNotWait);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ namespace BizHawk.Core
|
|||
EventWaitHandle ewh;
|
||||
volatile bool killSignal;
|
||||
|
||||
public Func<Bitmap,bool> ReleaseCallback;
|
||||
|
||||
/// <summary>
|
||||
/// Turns this panel into multi-threaded mode.
|
||||
/// This will sort of glitch out other gdi things on the system, but at least its fast...
|
||||
|
@ -89,7 +91,13 @@ namespace BizHawk.Core
|
|||
lock (this)
|
||||
{
|
||||
while (DisposeQueue.Count > 0)
|
||||
DisposeQueue.Dequeue().Dispose();
|
||||
{
|
||||
var bmp = DisposeQueue.Dequeue();
|
||||
bool dispose = true;
|
||||
if(ReleaseCallback != null)
|
||||
dispose = ReleaseCallback(bmp);
|
||||
if(dispose) bmp.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue