csharp/aglab2/SM64StarDisplay/StarManager/ViewHelper/LayoutDescriptionEx.cs

LayoutDescriptionEx.cs
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StarDisplay
{
    public clast LineDescriptionExSpecifiedConcreteClastConverter : DefaultContractResolver
    {
        protected override JsonConverter ResolveContractConverter(Type objectType)
        {
            if (typeof(LineDescriptionEx).IsastignableFrom(objectType) && !objectType.IsAbstract)
                return null; // pretend TableSortRuleConvert is not specified (thus avoiding a stack overflow)

            return base.ResolveContractConverter(objectType);
        }
    }

    public clast LineDescriptionExConverter : JsonConverter
    {
        static JsonSerializerSettings SpecifiedSubclastConversion = new JsonSerializerSettings()
        { ContractResolver = new LineDescriptionExSpecifiedConcreteClastConverter() };

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(LineDescriptionEx);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
                return null;

            JObject jo = JObject.Load(reader);
            switch (jo["Type"].Value())
            {
                case 1:
                    return JsonConvert.DeserializeObject(jo.ToString(), SpecifiedSubclastConversion);
                case 2:
                    return JsonConvert.DeserializeObject(jo.ToString(), SpecifiedSubclastConversion);
                default:
                    throw new Exception();
            }
        }
        public override bool CanWrite
        {
            get { return false; }
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException(); // won't be called because CanWrite returns false
        }
    }

    public clast ImageConverter : JsonConverter
    {
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            string text = (string)reader.Value;
            if (string.IsNullOrWhiteSpace(text))
            {
                return Resource.gold_star;
            }
            return Image.FromStream(new MemoryStream(Convert.FromBase64String(text)));
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            Image obj = (Image)value;
            MemoryStream memoryStream = new MemoryStream();
            obj.Save(memoryStream, ImageFormat.Png);
            byte[] value2 = memoryStream.ToArray();
            writer.WriteValue(value2);
        }

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(Image);
        }
    }

    [Serializable]
    [JsonConverter(typeof(LineDescriptionExConverter))]
    public abstract clast LineDescriptionEx
    {
        public abstract void DrawBase(GraphicsManager gm, int lineNumber, bool isSecret);
        public abstract bool IsEmpty();
        public abstract void FixType();
    }

    [Serializable]
    public clast TextOnlyLineDescription : LineDescriptionEx
    {
        public string text;
        
        public int Type = 2;

        public override void FixType()
        {
            Type = 2;
        }

        public TextOnlyLineDescription(string text)
        {
            this.text = text;
        }

        public override void DrawBase(GraphicsManager gm, int lineNumber, bool isSecret)
        {
            Font drawFont = new Font(gm.FontFamily, gm.DrawFontSize);

            SolidBrush drawBrush = new SolidBrush(Color.White);
            RectangleF drawRect = new RectangleF((isSecret ? (gm.Width / 2) : 0) + gm.SWidth / 2, lineNumber * gm.SHeight, gm.HalfWidth, gm.SHeight);
            StringFormat drawFormat = new StringFormat
            {
                Alignment = StringAlignment.Near,
                LineAlignment = StringAlignment.Center
            };
            gm.graphics.DrawStringWithOutline(text, gm.FontFamily, gm.DrawFontSize, drawBrush, Pens.Black, drawRect, drawFormat);
            drawBrush.Dispose();
            drawFont.Dispose();
        }

        public override bool IsEmpty()
        {
            return text == "";
        }
    }

    [Serializable]
    public clast StarsLineDescription : LineDescriptionEx
    {
        public string text;

        public byte starMask;
        public int offset;

        public byte highlightStarMask;
        public int highlightOffset;

        public int Type = 1;

        public override void FixType()
        {
            Type = 1;
        }

        public StarsLineDescription(string text, byte starMask, int offset, byte highlightStarMask, int highlightOffset)
        {
            this.text = text;
            this.starMask = starMask;
            this.offset = offset;
            this.highlightStarMask = highlightStarMask;
            this.highlightOffset = highlightOffset;
        }

        public override void DrawBase(GraphicsManager gm, int lineNumber, bool isSecret)
        {
            SolidBrush drawBrush = new SolidBrush(Color.White);
            RectangleF drawRect = new RectangleF((isSecret ? (gm.Width / 2) : 0), lineNumber * gm.SHeight, gm.HalfWidth, gm.SHeight);
            StringFormat drawFormat = new StringFormat
            {
                Alignment = StringAlignment.Near,
                LineAlignment = StringAlignment.Center
            };
            gm.graphics.DrawStringWithOutline(text, gm.FontFamily, gm.DrawFontSize, drawBrush, Pens.Black, drawRect, drawFormat);
            gm.DrawByte(0, lineNumber, isSecret, starMask);

            drawBrush.Dispose();
        }

        public override bool IsEmpty()
        {
            return starMask == 0 && highlightStarMask == 0 && text == "";
        }
    }
    
    [Serializable]
    public clast LayoutDescriptionEx
    {
        public List courseDescription;
        public List secretDescription;

        [JsonConverter(typeof(ImageConverter))]
        public Bitmap goldStar;
        [JsonConverter(typeof(ImageConverter))]
        public Bitmap darkStar;
        [JsonConverter(typeof(ImageConverter))]
        public Bitmap redOutline;
        [JsonConverter(typeof(ImageConverter))]
        public Bitmap greenOutline;
        [JsonConverter(typeof(ImageConverter))]
        public Bitmap invertedStar;

        public string starAmount;

        public int starsShown;

        public bool useEmptyStars;

        // JSON
        public LayoutDescriptionEx()
        {
        }

        public LayoutDescriptionEx(List courseDescription, List secretDescription, Bitmap star, string starAmount, int starsShown)
        {
            this.starsShown = starsShown;

            this.courseDescription = new List(courseDescription);
            this.secretDescription = new List(secretDescription);
            this.starAmount = starAmount;

            this.goldStar = star;
            this.darkStar = new Bitmap(goldStar.Width, goldStar.Height);
            if (goldStar.Width != 20 || goldStar.Height != 20)
                Compress();

            GenerateInvertedStar();
            GenerateDarkStar();
            GenerateOutline();

            Trim();
        }

        public bool isValid()
        {
            return courseDescription == null || secretDescription == null;
        }

        public void GenerateInvertedStar()
        {
            invertedStar = ImageProcessing.Invert(goldStar);
        }

        public void GenerateDarkStar()
        {
            darkStar = ImageProcessing.Desaturate(goldStar);
            if (useEmptyStars)
                darkStar = ImageProcessing.GetEmptied(darkStar, 2);
        }

        public void GenerateOutline()
        {
            this.redOutline = new Bitmap(goldStar.Width, goldStar.Height);
            this.greenOutline = new Bitmap(goldStar.Width, goldStar.Height);

            int[,] outlineAlpha = ImageProcessing.GetAlpha(goldStar);
            for (int i = 0; i < 10; i++)
            {
                outlineAlpha = ImageProcessing.OutlineAlpha(outlineAlpha, goldStar);
            }
            for (int i = 0; i < goldStar.Width; i++)
            {
                for (int j = 0; j < goldStar.Height; j++)
                {
                    redOutline.SetPixel(i, j, Color.FromArgb(outlineAlpha[i, j], 255, 0, 0));
                    greenOutline.SetPixel(i, j, Color.FromArgb(outlineAlpha[i, j], 0, 255, 0));
                }
            }
        }

        public void SaturateStar()
        {
            ImageProcessing.Saturate(goldStar);
        }

        public void Trim()
        {
            while (courseDescription.Count() != 0)
            {
                LineDescriptionEx lde = courseDescription.Last();

                if (lde == null || lde.IsEmpty())
                    courseDescription.RemoveAt(courseDescription.Count() - 1);
                else
                    break;
            }

            while (secretDescription.Count() != 0)
            {
                LineDescriptionEx lde = secretDescription.Last();

                if (lde == null || lde.IsEmpty())
                    secretDescription.RemoveAt(secretDescription.Count() - 1);
                else
                    break;
            }

            foreach (var ld in courseDescription)
            {
                if (ld is object)
                    ld.FixType();
            }
            foreach (var ld in secretDescription)
            {
                if (ld is object)
                    ld.FixType();
            }
        }

        public void PrepareForEdit()
        {
            Trim();
            int courseDescriptionLength = courseDescription.Count();
            int secretDescriptionLength = secretDescription.Count();
            int length = Math.Max(courseDescriptionLength, secretDescriptionLength);
            for (int currentIndex = courseDescriptionLength; currentIndex