BizHawk/Bizware/BizHawk.Bizware.BizwareGL/ArtManager.cs

165 lines
5.0 KiB
C#

using System;
using System.IO;
using System.Collections.Generic;
namespace BizHawk.Bizware.BizwareGL
{
/// <summary>
/// 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
/// </summary>
public class ArtManager : IDisposable
{
public ArtManager(IGL owner)
{
Owner = owner;
Open();
}
public void Dispose()
{
//todo
}
/// <summary>
/// Reopens this instance for further resource loading. Fails if it has been closed forever.
/// </summary>
public void Open()
{
AssertIsOpen(false);
if (IsClosedForever) throw new InvalidOperationException($"{nameof(ArtManager)} instance has been closed forever!");
IsOpened = true;
}
/// <summary>
/// Loads the given stream as an Art instance
/// </summary>
public Art LoadArt(Stream stream)
{
return LoadArtInternal(new BitmapBuffer(stream, new BitmapLoadOptions()));
}
/// <summary>
/// Loads the given path as an Art instance.
/// </summary>
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;
}
/// <summary>
/// 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
/// </summary>
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<TexAtlas.RectItem> atlasItems = new List<TexAtlas.RectItem>();
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<Art, BitmapBuffer>)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;
}
/// <summary>
/// Throws an exception if the instance is not open
/// </summary>
private void AssertIsOpen(bool state) { if (IsOpened != state) throw new InvalidOperationException($"{nameof(ArtManager)} instance is not open!"); }
public IGL Owner { get; private set; }
public bool IsOpened { get; private set; }
public bool IsClosedForever { get; private set; }
/// <summary>
/// This is used to remember the original bitmap sources for art files. Once the ArtManager is closed forever, this will be purged
/// </summary>
Dictionary<Art, BitmapBuffer> ArtLooseTextureAssociation = new Dictionary<Art, BitmapBuffer>();
/// <summary>
/// Physical texture resources, which exist after this ArtManager has been closed
/// </summary>
List<Texture2d> ManagedTextures = new List<Texture2d>();
/// <summary>
/// All the Arts managed by this instance
/// </summary>
List<Art> ManagedArts = new List<Art>();
}
}