LineTransform ILineTransformSource.GetLineTransform(ITextViewLine line, double yPosition, ViewRelativePosition placement)
        {
#pragma warning disable 219
            bool imageOnLine = false; // useful for tracing
#pragma warning restore 219

            int           lineNumber = line.Snapshot.GetLineFromPosition(line.Start.Position).LineNumber;
            LineTransform lineTransform;

            // Look up Image for current line and increase line height as necessary
            if (_manager.Images.ContainsKey(lineNumber) && ImageAdornmentManager.Enabled)
            {
                double  defaultHeight = line.DefaultLineTransform.BottomSpace;
                MyImage image         = _manager.Images[lineNumber];
                lineTransform = new LineTransform(0, image.Height + defaultHeight, 1.0);

                imageOnLine = true;
            }
            else
            {
                lineTransform = new LineTransform(0, 0, 1.0);
            }
            return(lineTransform);
        }
        /// <summary>
        /// Scans text line for matching image comment signature, then adds new or updates existing image adornment
        /// </summary>
        private void CreateVisuals(ITextViewLine line, int lineNumber)
        {
#pragma warning disable 219
            bool imageDetected = false; // useful for tracing
#pragma warning restore 219

            string lineText = line.Extent.GetText();
            string matchedText;
            int    matchIndex = ImageCommentParser.Match(_contentTypeName, lineText, out matchedText);
            if (matchIndex >= 0)
            {
                // Get coordinates of text
                int start = line.Extent.Start.Position + matchIndex;
                int end   = line.Start + (line.Extent.Length - 1);
                var span  = new SnapshotSpan(_view.TextSnapshot, Span.FromBounds(start, end));

                Exception xmlParseException;
                string    imageUrl;
                double    scale;
                Color     bgColor = new Color();
                ImageCommentParser.TryParse(matchedText, out imageUrl, out scale, ref bgColor, out xmlParseException);

                if (xmlParseException != null)
                {
                    if (Images.ContainsKey(lineNumber))
                    {
                        _layer.RemoveAdornment(Images[lineNumber]);
                        Images.Remove(lineNumber);
                    }

                    _errorTags.Add(new TagSpan <ErrorTag>(span, new ErrorTag("XML parse error", GetErrorMessage(xmlParseException))));

                    return;
                }

                MyImage   image;
                Exception imageLoadingException = null;

                // Check for and update existing image
                MyImage existingImage = Images.ContainsKey(lineNumber) ? Images[lineNumber] : null;
                if (existingImage != null)
                {
                    image = existingImage;
                    if (existingImage.Url != imageUrl || existingImage.BgColor != bgColor) // URL different, so set new source
                    {
                        existingImage.TrySet(imageUrl, scale, bgColor, out imageLoadingException, () => CreateVisuals(line, lineNumber));
                    }
                    else if (existingImage.Url == imageUrl && Math.Abs(existingImage.Scale - scale) > 0.0001)   // URL same but scale changed
                    {
                        existingImage.Scale = scale;
                    }
                }
                else // No existing image, so create new one
                {
                    image = new MyImage(_variableExpander);
                    image.TrySet(imageUrl, scale, bgColor, out imageLoadingException, () => CreateVisuals(line, lineNumber));
                    Images.Add(lineNumber, image);
                }

                // Position image and add as adornment
                if (imageLoadingException == null)
                {
                    Geometry g = _view.TextViewLines.GetMarkerGeometry(span);
                    if (g == null) // Exceptional case when image dimensions are massive (e.g. specifying very large scale factor)
                    {
                        throw new InvalidOperationException("Couldn't get source code line geometry. Is the loaded image massive?");
                    }
                    double textLeft   = g.Bounds.Left;
                    double textBottom = line.TextBottom;
                    Canvas.SetLeft(image, textLeft);
                    Canvas.SetTop(image, textBottom);

                    // Add image to editor view
                    try
                    {
                        _layer.RemoveAdornment(image);
                        _layer.AddAdornment(AdornmentPositioningBehavior.TextRelative, line.Extent, null, image, null);
                    }
                    catch (Exception ex)
                    {
                        // No expected exceptions, so tell user something is wrong.
                        ExceptionHandler.Notify(ex, true);
                    }
                }
                else
                {
                    if (Images.ContainsKey(lineNumber))
                    {
                        Images.Remove(lineNumber);
                    }

                    _errorTags.Add(new TagSpan <ErrorTag>(span, new ErrorTag("Trouble loading image", GetErrorMessage(imageLoadingException))));
                }
                imageDetected = true;
            }
            else
            {
                if (Images.ContainsKey(lineNumber))
                {
                    Images.Remove(lineNumber);
                }
            }
        }
        private void ShowDownloadedImage(ITextViewLine line, SnapshotSpan span, Color bgColor, MyImage image)
        {
            if (bgColor.A != 0 && image.BgColor != bgColor)
            {
                image.Scale   = 1;
                image.Source  = image.ReplaceTransparency(image.Source, bgColor);
                image.BgColor = bgColor;
            }

            Geometry g = _view.TextViewLines.GetMarkerGeometry(span);

            if (g == null) // Exceptional case when image dimensions are massive (e.g. specifying very large scale factor)
            {
                throw new InvalidOperationException("Couldn't get source code line geometry. Is the loaded image massive?");
            }
            double textLeft   = g.Bounds.Left;
            double textBottom = line.TextBottom;

            Canvas.SetLeft(image, textLeft);
            Canvas.SetTop(image, textBottom);

            // Add image to editor view
            try
            {
                _layer.RemoveAdornment(image);
                _layer.AddAdornment(AdornmentPositioningBehavior.TextRelative, line.Extent, null, image, null);
            }
            catch (Exception ex)
            {
                // No expected exceptions, so tell user something is wrong.
                ExceptionHandler.Notify(ex, true);
            }
        }
        /// <summary>
        /// Scans text line for matching image comment signature, then adds new or updates existing image adornment
        /// </summary>
        private void CreateVisuals(ITextViewLine line, int lineNumber)
        {
#pragma warning disable 219
            bool imageDetected = false; // useful for tracing
#pragma warning restore 219

            string lineText = line.Extent.GetText();
            string matchedText;
            int    matchIndex = ImageCommentParser.Match(_contentTypeName, lineText, out matchedText);
            if (matchIndex >= 0)
            {
                // Get coordinates of text
                int start = line.Extent.Start.Position + matchIndex;
                int end   = line.Start + (line.Extent.Length - 1);
                var span  = new SnapshotSpan(_view.TextSnapshot, Span.FromBounds(start, end));

                Exception xmlParseException;
                string    imageUrl;
                double    scale;
                Color     bgColor = new Color();
                ImageCommentParser.TryParse(matchedText, out imageUrl, out scale, ref bgColor, out xmlParseException);

                if (xmlParseException != null)
                {
                    if (Images.ContainsKey(lineNumber))
                    {
                        _layer.RemoveAdornment(Images[lineNumber]);
                        Images.Remove(lineNumber);
                    }

                    _errorTags.Add(new TagSpan <ErrorTag>(span, new ErrorTag("XML parse error", GetErrorMessage(xmlParseException))));

                    return;
                }

                MyImage   image;
                Exception imageLoadingException = null;

                // Check for and update existing image
                MyImage existingImage = Images.ContainsKey(lineNumber) ? Images[lineNumber] : null;
                if (existingImage != null)
                {
                    image = existingImage;
                    if (existingImage.Url != imageUrl) // URL different, so set new source
                    {
                        existingImage.TrySet(imageUrl, scale, bgColor, out imageLoadingException, () => CreateVisuals(line, lineNumber));
                        if (image.BgColor != null)
                        {
                            image.BgColor.R = 254;
                        }
                    }
                }
                else // No existing image, so create new one
                {
                    image = new MyImage(_variableExpander);
                    image.TrySet(imageUrl, scale, bgColor, out imageLoadingException, () => CreateVisuals(line, lineNumber));
                    if (image.BgColor != null)
                    {
                        image.BgColor.R = 254;
                    }

                    Images.Add(lineNumber, image);
                }

                // Position image and add as adornment
                if (imageLoadingException == null)
                {
                    try
                    {
                        _layer.RemoveAdornment(image);
                    }
                    catch (Exception ex)
                    {
                        // No expected exceptions, so tell user something is wrong.
                        ExceptionHandler.Notify(ex, true);
                    }

                    if ((image.Source as BitmapFrame)?.IsDownloading ?? false)
                    {
                        (image.Source as BitmapFrame).DownloadCompleted += (x, y) =>
                        {
                            if (x == image.Source)
                            {
                                ShowDownloadedImage(line, span, bgColor, image);
                            }
                        };
                    }
                    else
                    {
                        ShowDownloadedImage(line, span, bgColor, image);
                    }
                }
                else
                {
                    if (Images.ContainsKey(lineNumber))
                    {
                        Images.Remove(lineNumber);
                    }

                    _errorTags.Add(new TagSpan <ErrorTag>(span, new ErrorTag("Trouble loading image", GetErrorMessage(imageLoadingException))));
                }
                imageDetected = true;
            }
            else
            {
                if (Images.ContainsKey(lineNumber))
                {
                    Images.Remove(lineNumber);
                }
            }
        }