using System; using System.IO; using System.Collections.Generic; namespace BizHawk.Bizware.BizwareGL { /// /// Load resources through here, and they can be grouped together, for purposes of batching and whatnot. /// You can't use any of the returned Art resources until calling Close on the ArtManager /// public class ArtManager : IDisposable { public ArtManager(IGL owner) { Owner = owner; Open(); } public void Dispose() { //todo } /// /// Reopens this instance for further resource loading. Fails if it has been closed forever. /// public void Open() { AssertIsOpen(false); if (IsClosedForever) throw new InvalidOperationException("ArtManager instance has been closed forever!"); IsOpened = true; } /// /// Loads the given stream as an Art instance /// public Art LoadArt(Stream stream) { return LoadArtInternal(new BitmapBuffer(stream, new BitmapLoadOptions())); } /// /// Loads the given path as an Art instance. /// public Art LoadArt(string path) { using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) return LoadArtInternal(new BitmapBuffer(path, new BitmapLoadOptions())); } Art LoadArtInternal(BitmapBuffer tex) { AssertIsOpen(true); Art a = new Art(this); ArtLooseTextureAssociation[a] = tex; ManagedArts.Add(a); return a; } /// /// Closes this instance for for further resource loading. Will result in a texture atlasing operation. /// If the close operation is forever, then internal backup copies of resources will be freed, but it can never be reopened. /// This function may take some time to run, as it is /// public unsafe void Close(bool forever = true) { AssertIsOpen(true); IsOpened = false; IsClosedForever = forever; //first, cleanup old stuff foreach (var tex in ManagedTextures) tex.Dispose(); ManagedTextures.Clear(); //prepare input for atlas process and perform atlas //add 2 extra pixels for padding on all sides List atlasItems = new List(); foreach (var kvp in ArtLooseTextureAssociation) atlasItems.Add(new TexAtlas.RectItem(kvp.Value.Width+2, kvp.Value.Height+2, kvp)); var results = TexAtlas.PackAtlas(atlasItems); //this isnt supported yet: if (results.Atlases.Count > 1) throw new InvalidOperationException("Art files too big for atlas"); //prepare the output buffer BitmapBuffer bmpResult = new BitmapBuffer(results.Atlases[0].Size); //for each item, copy it into the output buffer and set the tex parameters on them for (int i = 0; i < atlasItems.Count; i++) { var item = results.Atlases[0].Items[i]; var artAndBitmap = (KeyValuePair)item.Item; var art = artAndBitmap.Key; var bitmap = artAndBitmap.Value; int w = bitmap.Width; int h = bitmap.Height; int dx = item.X + 1; int dy = item.Y + 1; for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { int pixel = bitmap.GetPixel(x, y); bmpResult.SetPixel(x+dx,y+dy,pixel); } var myDestBitmap = bmpResult; float myDestWidth = (float)myDestBitmap.Width; float myDestHeight = (float)myDestBitmap.Height; art.u0 = dx / myDestWidth; art.v0 = dy / myDestHeight; art.u1 = (dx + w) / myDestWidth; art.v1 = (dy + h) / myDestHeight; art.Width = w; art.Height = h; } //if we're closed forever, then forget all the original bitmaps if (forever) { foreach (var kvp in ArtLooseTextureAssociation) kvp.Value.Dispose(); ArtLooseTextureAssociation.Clear(); } //create a physical texture var texture = Owner.LoadTexture(bmpResult); ManagedTextures.Add(texture); //oops, we couldn't do this earlier. foreach (var art in ManagedArts) art.BaseTexture = texture; } /// /// Throws an exception if the instance is not open /// private void AssertIsOpen(bool state) { if (IsOpened != state) throw new InvalidOperationException("ArtManager instance is not open!"); } public IGL Owner { get; private set; } public bool IsOpened { get; private set; } public bool IsClosedForever { get; private set; } /// /// This is used to remember the original bitmap sources for art files. Once the ArtManager is closed forever, this will be purged /// Dictionary ArtLooseTextureAssociation = new Dictionary(); /// /// Physical texture resources, which exist after this ArtManager has been closed /// List ManagedTextures = new List(); /// /// All the Arts managed by this instance /// List ManagedArts = new List(); } }