using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Configuration; using System.Data; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; namespace RBG_Server { /// /// Interaction logic for App.xaml /// public partial class App : Application { public static App Context { get; private set; } public GameServer BoardGameServer {get;} public CommunicationHandler GameCommunicationHandler { get; private set;} protected static readonly SolidColorBrush RED_BRUSH = new(Color.FromRgb(255, 0, 0)); protected static readonly SolidColorBrush BLUE_BRUSH = new(Color.FromRgb(0, 0, 255)); protected static readonly SolidColorBrush GREEN_BRUSH = new(Color.FromRgb(0, 255, 0)); public const string MIP_LOW = "_mip_low"; public const string MIP_MEDIUM = "_mip_med"; public const string MIP_HIGH = "_mip_high"; public const string MIP_RAW = "_mip_raw"; /// /// Loads the specified files from disk, creating mip maps as appropriate /// /// /// /// void LoadFiles(string[] selectedFiles, bool isSprite=false, int spriteLimit=128) { // Continue in parallel _ = Parallel.ForEach(selectedFiles, (file) => { LoadFile(file); }); } /// /// Loads a singular file from disk, generating Mip maps as required /// /// void LoadFile(string path, bool isSprite=true) { // Check if the file is a .gif, first. using FileStream fs = File.OpenRead(path); LoadStream(fs, path, isSprite); } public void LoadStream(Stream source, string path, bool isSprite) { long srcStart = source.Position; byte[] vs = new byte[6]; // First 6 bytes is the gif specifier source.Read(vs, 0, 6); source.Position = srcStart; string magicNumber = Encoding.ASCII.GetString(vs); if (magicNumber == "GIF87a" || magicNumber == "GIF89a") { // Gif file, replace with a layered .png GifBitmapDecoder decoder = new(source, BitmapCreateOptions.None, BitmapCacheOption.Default); // Obtain each .gif frame BitmapFrame[] frames = new BitmapFrame[decoder.Frames.Count]; decoder.Frames.CopyTo(frames, 0); // Get frame sizes int height = frames[0].PixelHeight; int width = frames[0].PixelWidth; // The lowest mip is static, using either the thumb or first frame if (decoder.Thumbnail != null) { double scaleRate = 32 / (double)Math.Max(height, width); MemoryStream ms = ScaleImage(decoder.Thumbnail, scaleRate); _ = GameCommunicationHandler.ImageCollection.TryAdd(Path.GetFileNameWithoutExtension(path) + MIP_LOW + Path.GetExtension(path), new CachedByteArray(ms.ToArray())); } else { double scaleRate = 32 / (double)Math.Max(height, width); MemoryStream ms = ScaleImage(frames[0], scaleRate); _ = GameCommunicationHandler.ImageCollection.TryAdd(Path.GetFileNameWithoutExtension(path) + MIP_LOW + Path.GetExtension(path), new CachedByteArray(ms.ToArray())); } // The remaining mips are animated if (width >= 128 || height >= 128) { double scaleRate = 128 / (double)Math.Max(height, width); MemoryStream ms = ScaleAnimatedImage(frames, scaleRate); _ = GameCommunicationHandler.ImageCollection.TryAdd(Path.GetFileNameWithoutExtension(path) + MIP_MEDIUM + Path.GetExtension(path), new CachedByteArray(ms.ToArray())); } if (width >= 512 || height >= 512) { double scaleRate = 512 / (double)Math.Max(height, width); MemoryStream ms = ScaleAnimatedImage(frames, scaleRate); _ = GameCommunicationHandler.ImageCollection.TryAdd(Path.GetFileNameWithoutExtension(path) + MIP_HIGH + Path.GetExtension(path), new CachedByteArray(ms.ToArray())); } if (width >= 512 || height >= 512) { // Just re-encode the gif to .png PngBitmapEncoder encoder = new(); foreach (BitmapFrame frame in frames) { encoder.Frames.Add(frame); } MemoryStream ms = new(); encoder.Save(ms); _ = GameCommunicationHandler.ImageCollection.TryAdd(Path.GetFileNameWithoutExtension(path) + MIP_RAW + Path.GetExtension(path), new CachedByteArray(ms.ToArray())); } } else { // Regular image file // Create the bitmap to get the current intrinsics BitmapImage bitmapImage = new(); bitmapImage.BeginInit(); bitmapImage.StreamSource = source; bitmapImage.CacheOption = BitmapCacheOption.None; bitmapImage.EndInit(); int height = bitmapImage.PixelHeight; int width = bitmapImage.PixelWidth; // Behaviour depends on the required image if (isSprite) { // Sprites should have a low mip-map, and the medium // Low = 32*32 // Medium = 128*128 // High = 512*512 or image size if less than 1024 if (width >= 32 || height >= 32) { double scaleRate = 32 / (double)Math.Max(height, width); MemoryStream ms = ScaleImage(bitmapImage, scaleRate); _ = GameCommunicationHandler.ImageCollection.TryAdd(Path.GetFileNameWithoutExtension(path) + MIP_LOW + Path.GetExtension(path), new CachedByteArray(ms.ToArray())); } if (width >= 128 || height >= 128) { double scaleRate = 128 / (double)Math.Max(height, width); MemoryStream ms = ScaleImage(bitmapImage, scaleRate); _ = GameCommunicationHandler.ImageCollection.TryAdd(Path.GetFileNameWithoutExtension(path) + MIP_MEDIUM + Path.GetExtension(path), new CachedByteArray(ms.ToArray())); } if (width >= 512 || height >= 512) { double scaleRate = 512 / (double)Math.Max(height, width); MemoryStream ms = ScaleImage(bitmapImage, scaleRate); _ = GameCommunicationHandler.ImageCollection.TryAdd(Path.GetFileNameWithoutExtension(path) + MIP_HIGH + Path.GetExtension(path), new CachedByteArray(ms.ToArray())); } if (width >= 512 || height >= 512) { MemoryStream ms = new(); source.CopyTo(ms); _ = GameCommunicationHandler.ImageCollection.TryAdd(Path.GetFileNameWithoutExtension(path) + MIP_RAW + Path.GetExtension(path), new CachedByteArray(ms.ToArray())); } } else { // Non-sprites should have a low mip-map, and the medium, but at a larger size than the sprites // Low = 128*128 // Medium = 512*512 // High = 2048*2048 if (width >= 128 || height >= 128) { double scaleRate = 128 / (double)Math.Max(height, width); MemoryStream ms = ScaleImage(bitmapImage, scaleRate); _ = GameCommunicationHandler.ImageCollection.TryAdd(Path.GetFileNameWithoutExtension(path) + MIP_LOW + Path.GetExtension(path), new CachedByteArray(ms.ToArray())); } if (width >= 512 || height >= 512) { double scaleRate = 512 / (double)Math.Max(height, width); MemoryStream ms = ScaleImage(bitmapImage, scaleRate); _ = GameCommunicationHandler.ImageCollection.TryAdd(Path.GetFileNameWithoutExtension(path) + MIP_MEDIUM + Path.GetExtension(path), new CachedByteArray(ms.ToArray())); } if (width >= 2048 || height >= 2048) { double scaleRate = 2048 / (double)Math.Max(height, width); MemoryStream ms = ScaleImage(bitmapImage, scaleRate); _ = GameCommunicationHandler.ImageCollection.TryAdd(Path.GetFileNameWithoutExtension(path) + MIP_HIGH + Path.GetExtension(path), new CachedByteArray(ms.ToArray())); } if (width >= 2048 || height >= 2048) { MemoryStream ms = new(); source.CopyTo(ms); _ = GameCommunicationHandler.ImageCollection.TryAdd(Path.GetFileNameWithoutExtension(path) + MIP_RAW + Path.GetExtension(path), new CachedByteArray(ms.ToArray())); } } } } MemoryStream ScaleImage(BitmapSource source, double scaleRate) { TransformedBitmap modified = new(source, new ScaleTransform(source.PixelWidth * scaleRate, source.PixelHeight * scaleRate)); PngBitmapEncoder encoder = new(); encoder.Frames.Add(BitmapFrame.Create(modified)); MemoryStream ms = new(); encoder.Save(ms); return ms; } MemoryStream ScaleAnimatedImage(BitmapFrame[] frames, double scaleRate) { PngBitmapEncoder encoder = new(); foreach (BitmapFrame frame in frames) { TransformedBitmap modified = new(frame, new ScaleTransform(frame.PixelWidth * scaleRate, frame.PixelHeight * scaleRate)); encoder.Frames.Add(BitmapFrame.Create(modified)); } MemoryStream ms = new(); encoder.Save(ms); return ms; } } }