Esempio n. 1
0
        /// <summary>
        /// YTSubConverter supports multiple shadow types (and colors) on one subtitle by duplicating it as necessary
        /// </summary>
        private int ExpandLineForMultiShadows(int lineIndex)
        {
            Line line          = Lines[lineIndex];
            int  maxNumShadows = line.Sections.Max(s => s.ShadowColors.Count);

            if (maxNumShadows <= 1)
            {
                return(1);
            }

            List <List <ShadowType> > lineLayerShadowTypes = new List <List <ShadowType> >();

            ShadowType[] orderedShadowTypes = { ShadowType.SoftShadow, ShadowType.HardShadow, ShadowType.Bevel, ShadowType.Glow };
            foreach (Section section in line.Sections)
            {
                List <ShadowType> sectionLayerShadowTypes = new List <ShadowType>();
                foreach (ShadowType shadowType in orderedShadowTypes)
                {
                    if (section.ShadowColors.ContainsKey(shadowType))
                    {
                        sectionLayerShadowTypes.Add(shadowType);
                    }
                }
                lineLayerShadowTypes.Add(sectionLayerShadowTypes);
            }

            Lines.RemoveAt(lineIndex);

            for (int layerIdx = 0; layerIdx < maxNumShadows; layerIdx++)
            {
                Line shadowLine = (Line)line.Clone();
                for (int sectionIdx = 0; sectionIdx < shadowLine.Sections.Count; sectionIdx++)
                {
                    Section           section = shadowLine.Sections[sectionIdx];
                    List <ShadowType> sectionLayerShadowTypes = lineLayerShadowTypes[sectionIdx];

                    if (layerIdx > 0)
                    {
                        section.BackColor = ColorUtil.ChangeColorAlpha(section.BackColor, 0);
                    }

                    if (layerIdx < maxNumShadows - 1)
                    {
                        section.ForeColor = ColorUtil.ChangeColorAlpha(section.ForeColor, 0);
                    }

                    if (layerIdx < sectionLayerShadowTypes.Count)
                    {
                        section.ShadowColors.RemoveAll(t => t != sectionLayerShadowTypes[layerIdx]);
                    }
                    else
                    {
                        section.ShadowColors.Clear();
                    }
                }
                Lines.Insert(lineIndex + layerIdx, shadowLine);
            }

            return(maxNumShadows);
        }
Esempio n. 2
0
        /// <summary>
        /// The mobile apps have an unchangeable black background, meaning dark text is unreadable there.
        /// As a workaround, we overlap the dark subtitle with an invisible bright one: this way, we
        /// get the custom background and dark text on PC, and a black background and bright text
        /// on Android (because the Android app doesn't support transparency).
        /// Sadly, this trick doesn't work for iOS: that one supports (only) text transparency,
        /// meaning our bright yet invisible subtitle doesn't show up there.
        /// </summary>
        private int ExpandLineForDarkText(int lineIdx)
        {
            Line line = Lines[lineIdx];

            if (!line.Sections.Any(s => s.ForeColor.A > 0 && ColorUtil.IsDark(s.ForeColor)))
            {
                return(1);
            }

            Line brightLine = (Line)line.Clone();

            foreach (Section section in brightLine.Sections)
            {
                if (section.ForeColor.A > 0 && ColorUtil.IsDark(section.ForeColor))
                {
                    section.ForeColor = ColorUtil.Brighten(section.ForeColor);
                }

                section.ForeColor = ColorUtil.ChangeColorAlpha(section.ForeColor, 0);
                section.BackColor = ColorUtil.ChangeColorAlpha(section.BackColor, 0);
                section.ShadowColors.Clear();
            }

            Lines.Insert(lineIdx + 1, brightLine);
            return(2);
        }
        public override void Handle(AssTagContext context, string arg)
        {
            int alpha = !string.IsNullOrEmpty(arg) ? 255 - (ParseHex(arg) & 255) : context.Style.PrimaryColor.A;

            context.Section.ForeColor = ColorUtil.ChangeColorAlpha(context.Section.ForeColor, alpha);
            context.Section.Animations.RemoveAll(a => a is ForeColorAnimation);
        }
Esempio n. 4
0
        // Aegisub doesn't display the background box for certain parts of a subtitle:
        // - Whitespace at the start of a line of text
        // - Whitespace at the end of a line of text
        // - Sections that consist only of whitespace
        // YouTube displays the background box across the entire subtitle, however,
        // so for an accurate visualization, we need to enclose whitespace blocks in
        // other characters to "protect" them.
        private static void ProtectWhitespace(AssLine line)
        {
            for (int i = line.Sections.Count - 1; i >= 0; i--)
            {
                AssSection section = (AssSection)line.Sections[i];
                if (Regex.IsMatch(section.Text, @"^[\r\n]+$"))
                {
                    continue;
                }

                Match match = Regex.Match(section.Text, @"^([ \xA0]*)(.*?)([ \xA0]*)$");
                if (match.Groups[1].Length == section.Text.Length)
                {
                    // If the entire section is just whitespace, we have to use a non-whitespace character to make its background visible
                    section.Text      = CreateProtectedWhitespaceString(section.Text.Length, ".");
                    section.ForeColor = ColorUtil.ChangeColorAlpha(section.ForeColor, 0);
                    section.ShadowColors.Clear();
                }
                else
                {
                    // Otherwise, we can use non-breaking spaces (which don't normally work, but do work in our case because we later write
                    // them out using Aegisub's \h sequence)
                    section.Text = CreateProtectedWhitespaceString(match.Groups[1].Length, "\xA0") + match.Groups[2].Value + CreateProtectedWhitespaceString(match.Groups[3].Length, "\xA0");
                }
            }
        }
Esempio n. 5
0
        public override void Handle(AssTagContext context, string arg)
        {
            int alpha = 255 - ParseHex(arg);

            context.Section.ForeColor = ColorUtil.ChangeColorAlpha(context.Section.ForeColor, alpha);
            context.Section.Animations.RemoveAll(a => a is ForeColorAnimation);
        }
Esempio n. 6
0
        private static Color MultiplyColorAlpha(Color color, float factor)
        {
            if (color.IsEmpty)
            {
                return(color);
            }

            return(ColorUtil.ChangeColorAlpha(color, (int)(color.A * factor)));
        }
        private static void HandleTransformShadowAlphaTag(AssTagContext context, ShadowType shadowType, DateTime startTime, DateTime endTime, float accel, string arg)
        {
            ShadowColorAnimation anim = FetchColorAnimation(
                context,
                startTime,
                endTime,
                a => a.ShadowType == shadowType,
                s => s.ShadowColors[shadowType],
                (s, e, c) => new ShadowColorAnimation(shadowType, s, c, e, c, accel)
                );

            anim.EndColor = ColorUtil.ChangeColorAlpha(anim.EndColor, 255 - (ParseHex(arg) & 255));
        }
Esempio n. 8
0
        public override void Handle(AssTagContext context, string arg)
        {
            int alpha = 255 - ParseHex(arg);

            context.Section.ForeColor      = ColorUtil.ChangeColorAlpha(context.Section.ForeColor, alpha);
            context.Section.SecondaryColor = ColorUtil.ChangeColorAlpha(context.Section.SecondaryColor, alpha);
            context.Section.BackColor      = ColorUtil.ChangeColorAlpha(context.Section.BackColor, alpha);
            foreach (ShadowType shadowType in context.Section.ShadowColors.Keys.ToList())
            {
                context.Section.ShadowColors[shadowType] = ColorUtil.ChangeColorAlpha(context.Section.ShadowColors[shadowType], alpha);
            }

            context.Section.Animations.RemoveAll(a => a is ColorAnimation);
        }
Esempio n. 9
0
        private (int, Section) ReadPen(XmlElement elem)
        {
            int     id  = elem.GetIntAttribute("id") ?? 0;
            Section pen = new Section();

            int fontStyleId = elem.GetIntAttribute("fs") ?? 0;

            pen.Font  = GetFontName(fontStyleId);
            pen.Scale = GetRealFontScale(elem.GetIntAttribute("sz") ?? 100);

            pen.Offset    = GetOffsetType(elem.GetIntAttribute("of") ?? 1);
            pen.Bold      = Convert.ToBoolean(elem.GetIntAttribute("b") ?? 0);
            pen.Italic    = Convert.ToBoolean(elem.GetIntAttribute("i") ?? 0);
            pen.Underline = Convert.ToBoolean(elem.GetIntAttribute("u") ?? 0);

            Color fc = ColorUtil.FromHtml(elem.Attributes["fc"]?.Value ?? "#FFFFFF");
            int   fo = elem.GetIntAttribute("fo") ?? 254;

            pen.ForeColor = ColorUtil.ChangeColorAlpha(fc, fo);

            Color bc = ColorUtil.FromHtml(elem.Attributes["bc"]?.Value ?? "#080808");
            int   bo = elem.GetIntAttribute("bo") ?? 192;

            pen.BackColor = ColorUtil.ChangeColorAlpha(bc, bo);

            int et = elem.GetIntAttribute("et") ?? 0;

            if (et != 0)
            {
                ShadowType   shadowType = GetEdgeType(et);
                Color        shadowColor;
                XmlAttribute ecAttr = elem.Attributes["ec"];
                if (ecAttr != null)
                {
                    shadowColor = ColorUtil.ChangeColorAlpha(ColorUtil.FromHtml(ecAttr.Value), 254);
                }
                else
                {
                    shadowColor = Color.FromArgb(fo, 0x22, 0x22, 0x22);
                }

                pen.ShadowColors.Add(shadowType, shadowColor);
            }

            pen.RubyPart = GetRubyPart(elem.GetIntAttribute("rb") ?? 0);
            pen.Packed   = Convert.ToBoolean(elem.GetIntAttribute("hg") ?? 0);
            return(id, pen);
        }
        private static void HandleTransformOutlineAlphaTag(AssTagContext context, DateTime startTime, DateTime endTime, float accel, string arg)
        {
            if (!context.Style.HasOutline)
            {
                return;
            }

            if (context.Style.OutlineIsBox)
            {
                BackColorAnimation anim = FetchColorAnimation(context, startTime, endTime, s => s.BackColor, (s, e, c) => new BackColorAnimation(s, c, e, c, accel));
                anim.EndColor = ColorUtil.ChangeColorAlpha(anim.EndColor, 255 - (ParseHex(arg) & 255));
            }
            else
            {
                HandleTransformShadowAlphaTag(context, ShadowType.Glow, startTime, endTime, accel, arg);
            }
        }
        public override void Handle(AssTagContext context, string arg)
        {
            if (!context.Style.HasShadow)
            {
                return;
            }

            int alpha = 255 - ParseHex(arg);

            foreach (KeyValuePair <ShadowType, Color> shadowColor in context.Section.ShadowColors.ToList())
            {
                if (shadowColor.Key != ShadowType.Glow || !context.Style.HasOutline || context.Style.HasOutlineBox)
                {
                    context.Section.ShadowColors[shadowColor.Key] = ColorUtil.ChangeColorAlpha(shadowColor.Value, alpha);
                }
            }
            context.Section.Animations.RemoveAll(a => a is ShadowColorAnimation);
        }
Esempio n. 12
0
        /// <summary>
        /// We should never use 255 for a foreground or background opacity. The reason is that if we do, the upload process
        /// will remove the attribute from the file. For such removed attributes, the player is free to use its own
        /// default settings... which may not match what we want. For example, if the background opacity is lost because
        /// we set it to 255, the PC player will use its default setting of 191. What's more, YouTube allows users to
        /// customize the default settings - in a small window that nobody knows about, with keyboard shortcuts that are
        /// easy to trigger by accident. The result is that some users accidentally configured a low, hard-to-read
        /// foreground opacity and don't know how to change it back.
        /// In addition, we need to avoid pure white (#FFFFFF) for the foreground color because, even though this will
        /// remain in the file after uploading (if the foreground opacity is &lt; 255), the Android app will still
        /// ignore it and use the foreground color of the preceding section.
        /// </summary>
        private void LimitColors(int lineIndex)
        {
            Line line = Lines[lineIndex];

            foreach (Section section in line.Sections)
            {
                if ((section.ForeColor.ToArgb() & 0xFFFFFF) == 0xFFFFFF)
                {
                    section.ForeColor = Color.FromArgb(section.ForeColor.A << 24 | 0xFEFEFE);
                }

                if (section.ForeColor.A == 255)
                {
                    section.ForeColor = ColorUtil.ChangeColorAlpha(section.ForeColor, 254);
                }

                if (section.BackColor.A == 255)
                {
                    section.BackColor = ColorUtil.ChangeColorAlpha(section.BackColor, 254);
                }

                // YouTube doesn't have a shadow opacity attribute, so explicitly set the opacity to 255 to avoid creating
                // superfluous <pen>s later on (pens that only differ in shadow opacity). The exception is #222222 which
                // does support opacity, in a way.
                List <ShadowType> shadowTypesToChange = null;
                foreach (KeyValuePair <ShadowType, Color> shadowColor in section.ShadowColors)
                {
                    if (shadowColor.Value.A != 254 && ((shadowColor.Value.ToArgb() & 0xFFFFFF) != 0x222222 || shadowColor.Value.A != section.ForeColor.A))
                    {
                        (shadowTypesToChange ??= new List <ShadowType>()).Add(shadowColor.Key);
                    }
                }

                if (shadowTypesToChange == null)
                {
                    continue;
                }

                foreach (ShadowType shadowType in shadowTypesToChange)
                {
                    section.ShadowColors[shadowType] = ColorUtil.ChangeColorAlpha(section.ShadowColors[shadowType], 254);
                }
            }
        }
        public override void Handle(AssTagContext context, string arg)
        {
            if (!context.Style.HasOutline)
            {
                return;
            }

            int alpha = !string.IsNullOrEmpty(arg) ? 255 - (ParseHex(arg) & 255) : context.Style.OutlineColor.A;

            if (context.Style.OutlineIsBox)
            {
                context.Section.BackColor = ColorUtil.ChangeColorAlpha(context.Section.BackColor, alpha);
                context.Section.Animations.RemoveAll(a => a is BackColorAnimation);
            }
            else
            {
                context.Section.ShadowColors[ShadowType.Glow] = ColorUtil.ChangeColorAlpha(context.Section.ShadowColors[ShadowType.Glow], alpha);
                context.Section.Animations.RemoveAll(a => a is ShadowColorAnimation);
            }
        }
        private static void HandleTransformSecondaryAlphaTag(AssTagContext context, DateTime startTime, DateTime endTime, float accel, string arg)
        {
            SecondaryColorAnimation anim = FetchColorAnimation(context, startTime, endTime, s => s.SecondaryColor, (s, e, c) => new SecondaryColorAnimation(s, c, e, c, accel));

            anim.EndColor = ColorUtil.ChangeColorAlpha(anim.EndColor, 255 - (ParseHex(arg) & 255));
        }
        private static void HandleTransformForeAlphaTag(AssTagContext context, DateTime startTime, DateTime endTime, int accel, string arg)
        {
            ForeColorAnimation anim = FetchColorAnimation(context, startTime, endTime, s => s.ForeColor, (s, e, c) => new ForeColorAnimation(s, c, e, c));

            anim.EndColor = ColorUtil.ChangeColorAlpha(anim.EndColor, 255 - ParseHex(arg));
        }