Beispiel #1
0
        /// <summary>
        /// Draws an image frame that is centered within, masked and outlined by the specified <see
        /// cref="RegularPolygon"/>.</summary>
        /// <param name="context">
        /// The <see cref="DrawingContext"/> for the drawing.</param>
        /// <param name="brush">
        /// The <see cref="Brush"/> used to draw the <paramref name="polygon"/> outline.</param>
        /// <param name="target">
        /// The region within <paramref name="context"/> on which the copied image frame is
        /// centered.</param>
        /// <param name="sourceBitmap">
        /// A <see cref="WriteableBitmap"/> containing the image frame to copy.</param>
        /// <param name="source">
        /// The region within <paramref name="sourceBitmap"/> that covers the image frame to copy.
        /// </param>
        /// <param name="polygon">
        /// The <see cref="RegularPolygon"/> whose <see cref="RegularPolygon.Vertices"/> provide the
        /// clipping region and outline for the copied frame.</param>
        /// <param name="scalingX">
        /// An <see cref="ImageScaling"/> value indicating the horizontal scaling of the <paramref
        /// name="source"/> region to fit the specified <paramref name="polygon"/>.</param>
        /// <param name="scalingY">
        /// An <see cref="ImageScaling"/> value indicating the vertical scaling of the <paramref
        /// name="source"/> region to fit the specified <paramref name="polygon"/>.</param>
        /// <param name="colorShift">
        /// An optional <see cref="ColorVector"/> applied to all pixels within the drawing. Specify
        /// <see cref="ColorVector.Empty"/> if colors should remain unchanged.</param>
        /// <param name="offset">
        /// An optional pixel offset that is added to the centered location within the <paramref
        /// name="target"/> region.</param>
        /// <param name="scalingVector">
        /// An optional scaling vector that is applied to the transformation matrix of the specified
        /// <paramref name="context"/> object. Specify <see cref="PointI.Empty"/> if no scaling
        /// vector should be applied.</param>
        /// <returns>
        /// <c>true</c> if <see cref="CheckFrame"/> succeeded with the specified arguments;
        /// otherwise, <c>false</c>.</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="context"/> or <paramref name="polygon"/> is a null reference.
        /// </exception>
        /// <remarks>
        /// <b>DrawOutlineFrame</b> always masks off the specified <paramref name="context"/> with
        /// the specified <paramref name="polygon"/>, and superimposes its outline on the drawn
        /// frame using the specified <paramref name="brush"/>.</remarks>

        public static bool DrawOutlineFrame(DrawingContext context,
                                            Brush brush, Rect target, WriteableBitmap sourceBitmap, RectI source,
                                            RegularPolygon polygon, ImageScaling scalingX, ImageScaling scalingY,
                                            ColorVector colorShift, PointI offset, PointI scalingVector)
        {
            // check drawing parameters
            if (!CheckFrame(sourceBitmap, source, polygon))
            {
                return(false);
            }

            // compute frame bounds centered on target region
            RectD frameTarget = new RectD(
                target.X + (target.Width - polygon.Bounds.Width) / 2.0,
                target.Y + (target.Height - polygon.Bounds.Height) / 2.0,
                polygon.Bounds.Width, polygon.Bounds.Height);

            // mask off region outside polygon outline
            MaskOutline(context, target, polygon);

            // draw frame with specified display parameters
            DrawFrame(null, context, frameTarget, sourceBitmap, source,
                      scalingX, scalingY, colorShift, offset, scalingVector);

            // draw polygon outline around frame
            context.Pop();
            DrawOutline(context, brush, target, polygon);

            return(true);
        }
Beispiel #2
0
 static AspectRatioOption Map(ImageScaling scaling)
 {
     return(scaling switch
     {
         ImageScaling.FitWidth => AspectRatioOption.FitWidth,
         ImageScaling.FitHeight => AspectRatioOption.FitHeight,
         ImageScaling.FitArea => AspectRatioOption.FitArea,
         _ => throw new ArgumentOutOfRangeException()
     });
Beispiel #3
0
        /// <summary>
        /// Draws the <see cref="ImageStack"/> to the specified rectangle.</summary>
        /// <param name="context">
        /// The <see cref="DrawingContext"/> for the drawing.</param>
        /// <param name="target">
        /// The drawing region within <paramref name="context"/>, in device-independent pixels.
        /// </param>
        /// <returns>
        /// <c>true</c> if all <see cref="ImageStack"/> elements were drawn without errors;
        /// otherwise, <c>false</c>.</returns>
        /// <remarks>
        /// <b>DrawImageStack</b> draws the selected <see cref="Frames"/> of all <see
        /// cref="ImageStack"/> elements to the specified <paramref name="target"/> rectangle within
        /// the specified <paramref name="context"/>.</remarks>

        private bool DrawImageStack(DrawingContext context, Rect target)
        {
            Debug.Assert(ImageStack.Count == Frames.Count);

            // fail if no images are available
            if (ImageStack.Count == 0)
            {
                return(false);
            }

            // draw selected frames of all images
            for (int i = 0; i < ImageStack.Count; i++)
            {
                ImageStackEntry entry = ImageStack[i];

                // quit on first unavailable image
                EntityImage image = entry.Image.Value;
                if (image == null)
                {
                    return(false);
                }

                // determine selected frame
                int        frameIndex = Frames[i] % image.Frames.Count;
                ImageFrame frame      = image.Frames[frameIndex];

                // quit on first unavailable bitmap
                if (frame.Source.Value == null)
                {
                    return(false);
                }

                // get desired image scaling, unless disabled
                ImageScaling scalingX = ImageScaling.None, scalingY = ImageScaling.None;
                if (!entry.UseUnscaled)
                {
                    scalingX = image.ScalingX;
                    scalingY = image.ScalingY;
                }

                // draw frame with specified display parameters
                if (!ImageUtility.DrawOutlineFrame(context, Brushes.Black, target,
                                                   frame.Source.Value.Bitmap, frame.Bounds, Polygon, scalingX, scalingY,
                                                   entry.ColorShift, entry.Offset, entry.ScalingVector))
                {
                    return(false);
                }
            }

            return(true);
        }
Beispiel #4
0
        /// <summary>
        ///     Takes stream containing image and returns <see cref="Attachment" /> object.
        ///     This method doesn't compress image, it just calculates scaled dimensions
        ///     And saves the image.
        /// </summary>
        /// <param name="formFile"></param>
        /// <param name="imageName"></param>
        /// <param name="chatOrUserId"></param>
        /// <param name="sender"></param>
        /// <returns></returns>
        public async Task <MessageAttachment> SaveMessagePicture(IFormFile formFile,
                                                                 string imageName, string chatOrUserId, string sender)
        {
            try
            {
                using (var image = new MemoryStream())
                {
                    formFile.CopyTo(image);
                    image.Seek(0, SeekOrigin.Begin);

                    var resultDimensions =
                        ImageScaling.GetScaledDimensions(image, MessageImageMaxWidth, MessageImageMaxHeight);

                    image.Seek(0, SeekOrigin.Begin);

                    var resized = ImageCompression.Resize(image, resultDimensions.Item1, resultDimensions.Item2);

                    var imageNameWithoutExt = Path.GetFileNameWithoutExtension(imageName);

                    var extension = Path.GetExtension(imageName);

                    imageName = imageName.Length > MaxFileNameLength
                        ? imageNameWithoutExt.Substring(0, MaxFileNameLength - extension.Length) + extension
                        : imageName;

                    var resultPath = await SaveFile(formFile, resized, imageName, chatOrUserId, sender);

                    return(new MessageAttachment
                    {
                        AttachmentKind = AttachmentKind.Image,
                        AttachmentName = imageName,
                        ContentUrl = config["FileServer:Url"] + resultPath,
                        ImageHeight = resultDimensions.Item2,
                        ImageWidth = resultDimensions.Item1
                    });
                }
            }
            catch (ArgumentException e)
            {
                logger.LogError(e, "While resizing an image.");

                throw new Exception("Failed to upload this image.", e);
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "While resizing an image.");
                throw new Exception("Failed to upload this image.", ex);
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ChangeConnections"/> class.</summary>
        /// <param name="frame">
        /// The <see cref="ImageFrame"/> whose <see cref="ImageFrame.Connections"/> to edit.</param>
        /// <param name="bitmap">
        /// The <see cref="WriteableBitmap"/> containing the <see cref="ImageFrame.Bounds"/> of the
        /// specified <paramref name="frame"/>. This argument may be a null reference.</param>
        /// <param name="info">
        /// Informational text about the <paramref name="frame"/> to display in the dialog.</param>
        /// <param name="scalingX">
        /// An <see cref="ImageScaling"/> value indicating the horizontal scaling of the specified
        /// <paramref name="frame"/>.</param>
        /// <param name="scalingY">
        /// An <see cref="ImageScaling"/> value indicating the vertical scaling of the specified
        /// <paramref name="frame"/>.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="frame"/> is a null reference.</exception>
        /// <exception cref="ArgumentNullOrEmptyException">
        /// <paramref name="info"/> is a null reference or an empty string.</exception>

        public ChangeConnections(ImageFrame frame, WriteableBitmap bitmap,
                                 string info, ImageScaling scalingX, ImageScaling scalingY)
        {
            if (frame == null)
            {
                ThrowHelper.ThrowArgumentNullException("frame");
            }
            if (String.IsNullOrEmpty(info))
            {
                ThrowHelper.ThrowArgumentNullOrEmptyException("info");
            }

            this._frame = frame;
            InitializeComponent();

            // show specified frame information
            FrameInfo.Content = (string)FrameInfo.Content + " " + info;

            // show specified image frame
            FramePreview.Polygon  = MasterSection.Instance.Areas.MapGrid.Element;
            FramePreview.ScalingX = scalingX;
            FramePreview.ScalingY = scalingY;
            FramePreview.Show(frame, bitmap);

            // initialize directional toggle buttons
            this._compassControls = new CompassControl[] {
                new CompassControl(NorthToggle, Compass.North, Symbols.ArrowUp),
                new CompassControl(NorthEastToggle, Compass.NorthEast, Symbols.ArrowRightUp),
                new CompassControl(EastToggle, Compass.East, Symbols.ArrowRight),
                new CompassControl(SouthEastToggle, Compass.SouthEast, Symbols.ArrowRightDown),
                new CompassControl(SouthToggle, Compass.South, Symbols.ArrowDown),
                new CompassControl(SouthWestToggle, Compass.SouthWest, Symbols.ArrowLeftDown),
                new CompassControl(WestToggle, Compass.West, Symbols.ArrowLeft),
                new CompassControl(NorthWestToggle, Compass.NorthWest, Symbols.ArrowLeftUp)
            };

            // set checked states to Connections values
            foreach (Compass compass in frame.Connections)
            {
                int index = CompassToIndex(compass);
                this._compassControls[index].Button.IsChecked = true;
            }

            // disable controls invalid for current polygon
            EnableControls();
        }
Beispiel #6
0
        /// <summary>
        /// Copies the specified <see cref="ImageFrame"/> to the specified catalog tile.</summary>
        /// <param name="index">
        /// The catalog tile index where to copy the specified <paramref name="frame"/>.</param>
        /// <param name="entry">
        /// The <see cref="ImageStackEntry"/> whose <see cref="ImageStackEntry.Image"/> contains the
        /// specified <paramref name="frame"/>.</param>
        /// <param name="frame">
        /// The <see cref="ImageFrame"/> specifying the <see cref="ImageFrame.Source"/> bitmap and
        /// the <see cref="ImageFrame.Bounds"/> to copy.</param>
        /// <returns>
        /// <c>true</c> if copying succeeded; otherwise, <c>false</c>.</returns>

        private bool CopyImageFrame(int index, ImageStackEntry entry, ImageFrame frame)
        {
            // retrieve image file bitmap
            WriteableBitmap bitmap = frame.Source.Value.Bitmap;

            if (bitmap == null)
            {
                return(false);
            }

            // check for invalid frame bounds
            RectI source = frame.Bounds;

            if (source.X < 0 || source.Y < 0 || source.Width < 1 || source.Height < 1 ||
                source.X + source.Width > bitmap.Width ||
                source.Y + source.Height > bitmap.Height)
            {
                return(false);
            }

            // get desired image scaling, unless disabled
            ImageScaling scalingX = ImageScaling.None, scalingY = ImageScaling.None;

            if (!entry.UseUnscaled)
            {
                scalingX = entry.Image.Value.ScalingX;
                scalingY = entry.Image.Value.ScalingY;
            }

            // determine bounds of catalog tile
            RectI target     = GetTileBounds(index);
            RectI tileBounds = new RectI(0, 0, target.Width, target.Height);

            // draw frame with specified display parameters
            TileCopyBuffer.Clear();
            ImageUtility.DrawFrame(TileCopyBuffer, null, tileBounds, bitmap, source,
                                   scalingX, scalingY, entry.ColorShift, entry.Offset, entry.ScalingVector);

            // perform alpha blending with catalog bitmap
            Catalog.Overlay(target.X, target.Y, TileCopyBuffer, tileBounds);
            return(true);
        }
Beispiel #7
0
        private static void Image(this IContainer parent, SKImage image, ImageScaling scaling = ImageScaling.FitWidth)
        {
            if (image == null)
            {
                throw new DocumentComposeException("Cannot load or decode provided image.");
            }

            var aspectRatio = image.Width / (float)image.Height;

            var imageElement = new Image
            {
                InternalImage = image
            };

            if (scaling != ImageScaling.Resize)
            {
                parent = parent.AspectRatio(aspectRatio, Map(scaling));
            }

            parent.Element(imageElement);
Beispiel #8
0
        /// <summary>
        /// Handles the <see cref="Selector.SelectionChanged"/> event for the "X-Scaling" and
        /// "Y-Scaling" <see cref="ComboBox"/> controls.</summary>
        /// <param name="sender">
        /// The <see cref="Object"/> where the event handler is attached.</param>
        /// <param name="args">
        /// A <see cref="SelectionChangedEventArgs"/> object containing event data.</param>
        /// <remarks>
        /// <b>OnScalingSelected</b> updates the "Image Frames" list box with the new <see
        /// cref="ImageScaling"/> value for the selected dimension, and sets the <see
        /// cref="DataChanged"/> flag if it is different from the current <see
        /// cref="EntityImage.ScalingX"/> or <see cref="EntityImage.ScalingY"/> value.</remarks>

        private void OnScalingSelected(object sender, SelectionChangedEventArgs args)
        {
            args.Handled = true;
            if (!this._initialized)
            {
                return;
            }

            // retrieve current scaling option
            ComboBox     combo      = (ComboBox)sender;
            bool         isScalingX = (combo == ScalingXCombo);
            ImageScaling oldScaling = (isScalingX ? this._image.ScalingX : this._image.ScalingY);

            // get new scaling option, if any
            ImageScaling scaling = oldScaling;

            if (combo.SelectedItem != null)
            {
                scaling = (ImageScaling)combo.SelectedItem;
            }

            // update list box with new option
            if (isScalingX)
            {
                FrameList.ScalingX = scaling;
            }
            else
            {
                FrameList.ScalingY = scaling;
            }

            // broadcast data changes
            if (scaling != oldScaling)
            {
                DataChanged = true;
            }
        }
Beispiel #9
0
 /// <summary>
 ///     Default constructor
 /// </summary>
 /// <param name="vm"></param>
 public View(ImageScaling vm)
 {
     InitializeComponent();
     DataContext = vm;
 }
Beispiel #10
0
        /// <summary>
        /// Renders the visual content of the <see cref="ImageListBoxItem"/>.</summary>
        /// <param name="context">
        /// The <see cref="DrawingContext"/> for the rendering.</param>
        /// <remarks><para>
        /// <b>OnRender</b> calls the base class implementation of <see cref="UIElement.OnRender"/>,
        /// and then attempts to display the current <see cref="ContentControl.Content"/> of the
        /// <see cref="ImageListBoxItem"/>, depending on its type:
        /// </para><list type="bullet"><item>
        /// If the element is an <see cref="EntityImage"/>, its first image frame is drawn above a
        /// text label showing its identifier.
        /// </item><item>
        /// If the element is an <see cref="ImageFrame"/>, the frame is drawn without a label.
        /// </item><item>
        /// If the element is a <see cref="RectI"/>, the corresponding region of the <see
        /// cref="ImageListBox.FrameBitmap"/> is drawn without a label.
        /// </item></list><para>
        /// If the element is of any other type, or if an error occurs, the "invalid" icon returned
        /// by <see cref="Images.Invalid"/> is drawn instead.</para></remarks>

        protected override void OnRender(DrawingContext context)
        {
            base.OnRender(context);

            // default to base class if not in an ImageListBox
            ImageListBox listBox = Parent as ImageListBox;

            if (listBox == null)
            {
                return;
            }

            // get drawing parameters of containing ImageListBox
            WriteableBitmap bitmap = listBox.FrameBitmap;
            ImageScaling    scalingX = listBox.ScalingX, scalingY = listBox.ScalingY;

            // source within bitmap and target within context
            RectI source = RectI.Empty;
            Rect  target = new Rect(0, 0, ActualWidth, ActualHeight);

            /*
             * The default Background brush of a ListBoxItem is transparent, meaning that an item
             * will not register clicks on anything but the non-transparent image and text label.
             *
             * This is very inconvenient, so we always draw a fully opaque background. We use the
             * ListBox.Background brush for regular items, and the system-defined HighlightBrush
             * for selected items. The Foreground brush changes automatically as appropriate.
             *
             * NOTE: The HighlightBrush must be drawn explicitly anyway, or else the Foreground
             * of a selected item would be invisible against the Background of the ListBox!
             */

            Brush background = (listBox.SelectedItem == this ?
                                SystemColors.HighlightBrush : listBox.Background);

            context.DrawRectangle(background, null, target);

            // check which content we're drawing
            if (Content is RectI)
            {
                source = (RectI)Content;
            }
            else
            {
                ImageFrame frame = Content as ImageFrame;
                if (frame == null)
                {
                    EntityImage image = Content as EntityImage;
                    if (image == null)
                    {
                        goto failure;
                    }

                    // get first image frame and scaling
                    frame    = image.Frames[0];
                    scalingX = image.ScalingX;
                    scalingY = image.ScalingY;

                    // compute bounds of image label
                    Point textLocation = new Point(0, ActualHeight - this._labelHeight);
                    Size  textSize     = new Size(ActualWidth, this._labelHeight);

                    // reduce image bounds accordingly
                    target.Height -= textSize.Height;

                    // draw text for image label
                    FormattedText text = FormatLabel(image.Id, textSize);
                    context.DrawText(text, textLocation);
                }

                // final sanity check for valid image frame
                if (frame == null || frame.Source.Value == null)
                {
                    goto failure;
                }

                // draw image frame with associated bitmap
                bitmap = frame.Source.Value.Bitmap;
                source = frame.Bounds;
            }

            // draw specified rectangle with desired bitmap & scaling
            if (ImageUtility.DrawOutlineFrame(context, Foreground, target, bitmap, source,
                                              listBox.Polygon, scalingX, scalingY, ColorVector.Empty, PointI.Empty, PointI.Empty))
            {
                return;
            }

failure:
            // draw "invalid" icon on failure
            ImageUtility.DrawInvalidIcon(context, Foreground, target, listBox.Polygon);
        }
Beispiel #11
0
        /// <overloads>
        /// Draws the specified image frame to the specified <see cref="DrawingContext"/> or <see
        /// cref="WriteableBitmap"/>.</overloads>
        /// <summary>
        /// Draws the specified image frame to the specified <see cref="DrawingContext"/>.</summary>
        /// <param name="targetContext">
        /// The <see cref="DrawingContext"/> for the drawing.</param>
        /// <param name="target">
        /// The region within <paramref name="targetContext"/> on which the copied image frame is
        /// centered.</param>
        /// <param name="frameBitmap">
        /// A <see cref="WriteableBitmap"/> containing exactly the image frame to copy.</param>
        /// <param name="scalingX">
        /// An <see cref="ImageScaling"/> value indicating the horizontal scaling of the <paramref
        /// name="frameBitmap"/> to fit the specified <paramref name="target"/> region.</param>
        /// <param name="scalingY">
        /// An <see cref="ImageScaling"/> value indicating the vertical scaling of the <paramref
        /// name="frameBitmap"/> fit the specified <paramref name="target"/> region.</param>
        /// <param name="offset">
        /// An optional pixel offset that is added to the centered location within the <paramref
        /// name="target"/> region.</param>
        /// <param name="scalingVector">
        /// An optional scaling vector that is applied to the transformation matrix of the specified
        /// <paramref name="targetContext"/>. Specify <see cref="PointI.Empty"/> if no scaling
        /// vector should be applied.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="targetContext"/> or <paramref name="frameBitmap"/> is a null reference.
        /// </exception>
        /// <remarks>
        /// <b>DrawFrameCore</b> is called by <see cref="DrawFrame"/> when drawing to a <see
        /// cref="DrawingContext"/>, or to a <see cref="WriteableBitmap"/> with <see
        /// cref="ImageScaling.Stretch"/> scaling.</remarks>

        private static void DrawFrameCore(DrawingContext targetContext, RectD target,
                                          WriteableBitmap frameBitmap, ImageScaling scalingX, ImageScaling scalingY,
                                          PointI offset, PointI scalingVector)
        {
            if (targetContext == null)
            {
                ThrowHelper.ThrowArgumentNullException("targetContext");
            }
            if (frameBitmap == null)
            {
                ThrowHelper.ThrowArgumentNullException("frameBitmap");
            }

            // compute upper left corner of centered & offset frame
            SizeI  source = new SizeI(frameBitmap.PixelWidth, frameBitmap.PixelHeight);
            double x      = target.X + offset.X + (target.Width - source.Width) / 2.0;
            double y      = target.Y + offset.Y + (target.Height - source.Height) / 2.0;

            // default coordinates for single unscaled frame
            Rect  frame      = new Rect(0, 0, source.Width, source.Height);
            Point frameStart = new Point(x, y);
            Point frameStop  = new Point(x + source.Width, y + source.Height);

            // adjust horizontal coordinates for desired scaling
            switch (scalingX)
            {
            case ImageScaling.Repeat:
                while (frameStart.X > target.X)
                {
                    frameStart.X -= source.Width;
                }
                frameStop.X = target.Right;
                break;

            case ImageScaling.Stretch:
                frameStart.X = target.X + offset.X;
                frame.Width  = target.Width;
                break;
            }

            // adjust vertical coordinates for desired scaling
            switch (scalingY)
            {
            case ImageScaling.Repeat:
                while (frameStart.Y > target.Y)
                {
                    frameStart.Y -= source.Height;
                }
                frameStop.Y = target.Bottom;
                break;

            case ImageScaling.Stretch:
                frameStart.Y = target.Y + offset.Y;
                frame.Height = target.Height;
                break;
            }

            // apply scaling vector if specified
            double mx = 0, my = 0;

            if (scalingVector != PointI.Empty)
            {
                Matrix matrix = new Matrix();
                matrix.Scale(scalingVector.X, scalingVector.Y);
                targetContext.PushTransform(new MatrixTransform(matrix));

                // compensate for any coordinate inversion
                if (scalingVector.X < 0)
                {
                    mx = -2 * x - source.Width;
                }
                if (scalingVector.Y < 0)
                {
                    my = -2 * y - source.Height;
                }
            }

            // repeatedly copy frame to fill target bounds
            for (double dx = frameStart.X; dx < frameStop.X; dx += frame.Width)
            {
                for (double dy = frameStart.Y; dy < frameStop.Y; dy += frame.Height)
                {
                    frame.Location = new Point(dx + mx, dy + my);
                    targetContext.DrawImage(frameBitmap, frame);
                }
            }

            // pop scaling transformation, if any
            if (scalingVector != PointI.Empty)
            {
                targetContext.Pop();
            }
        }
Beispiel #12
0
        /// <summary>
        /// Draws an image frame that is centered within the specified <see cref="RectD"/>.
        /// </summary>
        /// <param name="targetBitmap">
        /// The <see cref="WriteableBitmap"/> for the drawing, assuming that <paramref
        /// name="targetContext"/> is a null reference.</param>
        /// <param name="targetContext">
        /// The <see cref="DrawingContext"/> for the drawing, assuming that <paramref
        /// name="targetBitmap"/> is a null reference.</param>
        /// <param name="target">
        /// The region within <paramref name="targetContext"/> on which the copied image frame is
        /// centered.</param>
        /// <param name="sourceBitmap">
        /// A <see cref="WriteableBitmap"/> containing the image frame to copy.</param>
        /// <param name="source">
        /// The region within <paramref name="sourceBitmap"/> that covers the image frame to copy.
        /// </param>
        /// <param name="scalingX">
        /// An <see cref="ImageScaling"/> value indicating the horizontal scaling of the <paramref
        /// name="source"/> region to fit the specified <paramref name="target"/> region.</param>
        /// <param name="scalingY">
        /// An <see cref="ImageScaling"/> value indicating the vertical scaling of the <paramref
        /// name="source"/> region to fit the specified <paramref name="target"/> region.</param>
        /// <param name="colorShift">
        /// An optional <see cref="ColorVector"/> applied to all pixels within the drawing. Specify
        /// <see cref="ColorVector.Empty"/> if colors should remain unchanged.</param>
        /// <param name="offset">
        /// An optional pixel offset that is added to the centered location within the <paramref
        /// name="target"/> region.</param>
        /// <param name="scalingVector">
        /// An optional scaling vector for the drawing. Specify <see cref="PointI.Empty"/> if no
        /// scaling vector should be applied.</param>
        /// <exception cref="ArgumentException">
        /// <paramref name="targetBitmap"/> and <paramref name="targetContext"/> are both valid or
        /// both null references.</exception>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="sourceBitmap"/> is a null reference.</exception>
        /// <remarks><para>
        /// <b>DrawFrame</b> expects that the specified <paramref name="target"/> region equals the
        /// <see cref="RegularPolygon.Bounds"/> of the desired <see cref="RegularPolygon"/> shape.
        /// <b>DrawFrame</b> does not apply a mask or draw an outline around the frame.
        /// </para><para>
        /// Either the specified <paramref name="targetBitmap"/> or the specified <paramref
        /// name="targetContext"/> must be valid, but not both. <b>DrawFrame</b> internally draws to
        /// a <see cref="WriteableBitmap"/> whenever possible to preserve visual quality, then
        /// copies the result to the specified drawing target if different. Alpha blending is
        /// performed only when drawing to a valid <paramref name="targetContext"/>.
        /// </para><para>
        /// If <paramref name="targetBitmap"/> is valid, it must be locked before <b>DrawFrame</b>
        /// is called. If <paramref name="targetContext"/> is valid, the specified <paramref
        /// name="scalingVector"/> is applied to its transformation matrix; otherwise, negative
        /// components indicate mirroring along the corresponding axis of the <paramref
        /// name="targetBitmap"/>.</para></remarks>

        internal static void DrawFrame(WriteableBitmap targetBitmap,
                                       DrawingContext targetContext, RectD target,
                                       WriteableBitmap sourceBitmap, RectI source,
                                       ImageScaling scalingX, ImageScaling scalingY,
                                       ColorVector colorShift, PointI offset, PointI scalingVector)
        {
            // exactly one drawing target must be valid
            if ((targetContext == null && targetBitmap == null) ||
                (targetContext != null && targetBitmap != null))
            {
                ThrowHelper.ThrowArgumentExceptionWithFormat(
                    "targetBitmap", Tektosyne.Strings.ArgumentConflict, targetContext);
            }

            if (sourceBitmap == null)
            {
                ThrowHelper.ThrowArgumentNullException("sourceBitmap");
            }

            // copy source tile to buffer bitmap
            var frameBitmap = new WriteableBitmap(
                source.Width, source.Height, 96, 96, PixelFormats.Pbgra32, null);

            frameBitmap.Lock();
            frameBitmap.Read(0, 0, sourceBitmap, source);

            // shift color channels if desired
            if (!colorShift.IsEmpty)
            {
                frameBitmap.Shift(colorShift.R, colorShift.G, colorShift.B);
            }

            frameBitmap.Unlock();

            // round target coordinates for bitmaps
            RectI targetI = target.Round();

            // use WPF drawing for Stretch scaling, else bitmap copying
            if (scalingX == ImageScaling.Stretch || scalingY == ImageScaling.Stretch)
            {
                // create intermediate context if necessary
                DrawingVisual visual = null;
                if (targetContext == null)
                {
                    visual        = new DrawingVisual();
                    targetContext = visual.RenderOpen();
                }

                // draw frame to target or intermediate context
                DrawFrameCore(targetContext, target, frameBitmap,
                              scalingX, scalingY, offset, scalingVector);

                // copy intermediate context to target bitmap
                if (visual != null)
                {
                    targetContext.Close();
                    var bitmap = new RenderTargetBitmap(
                        targetI.Right, targetI.Bottom, 96, 96, PixelFormats.Pbgra32);
                    bitmap.Render(visual);
                    targetBitmap.Read(targetI.X, targetI.Y, bitmap, targetI);
                }
            }
            else
            {
                // create intermediate bitmap if necessary
                if (targetContext != null)
                {
                    Debug.Assert(targetBitmap == null);
                    targetBitmap = new WriteableBitmap(
                        targetI.Right, targetI.Bottom, 96, 96, PixelFormats.Pbgra32, null);
                    targetBitmap.Lock();
                }

                // draw frame to target or intermediate bitmap
                DrawFrameCore(targetBitmap, targetI, frameBitmap,
                              scalingX, scalingY, offset, scalingVector);

                // copy intermediate bitmap to target context
                if (targetContext != null)
                {
                    targetBitmap.Unlock();
                    targetContext.DrawImage(targetBitmap,
                                            new Rect(0, 0, targetBitmap.Width, targetBitmap.Height));
                }
            }
        }
Beispiel #13
0
        /// <summary>
        /// Draws the specified image frame to the specified <see cref="WriteableBitmap"/>.
        /// </summary>
        /// <param name="targetBitmap">
        /// The <see cref="WriteableBitmap"/> for the drawing.</param>
        /// <param name="target">
        /// The region within <paramref name="targetBitmap"/> on which the copied image frame is
        /// centered.</param>
        /// <param name="frameBitmap">
        /// A <see cref="WriteableBitmap"/> containing exactly the image frame to copy.</param>
        /// <param name="scalingX">
        /// An <see cref="ImageScaling"/> value indicating the horizontal scaling of the <paramref
        /// name="frameBitmap"/> to fit the specified <paramref name="target"/> region.</param>
        /// <param name="scalingY">
        /// An <see cref="ImageScaling"/> value indicating the vertical scaling of the <paramref
        /// name="frameBitmap"/> fit the specified <paramref name="target"/> region.</param>
        /// <param name="offset">
        /// An optional pixel offset that is added to the centered location within the <paramref
        /// name="target"/> region.</param>
        /// <param name="scalingVector">
        /// An optional scaling vector whose negative components indicate the specified <paramref
        /// name="frameBitmap"/> should be mirrored along the corresponding axis. Specify zero or
        /// positive components for no mirroring.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="targetBitmap"/> or <paramref name="frameBitmap"/> is a null reference.
        /// </exception>
        /// <exception cref="InvalidEnumArgumentException">
        /// <paramref name="scalingX"/> or <paramref name="scalingY"/> equals <see
        /// cref="ImageScaling.Stretch"/>.</exception>
        /// <remarks>
        /// <b>DrawFrameCore</b> is called by <see cref="DrawFrame"/> when drawing to a <see
        /// cref="WriteableBitmap"/> without <see cref="ImageScaling.Stretch"/> scaling.</remarks>

        private static void DrawFrameCore(WriteableBitmap targetBitmap, RectI target,
                                          WriteableBitmap frameBitmap, ImageScaling scalingX, ImageScaling scalingY,
                                          PointI offset, PointI scalingVector)
        {
            if (targetBitmap == null)
            {
                ThrowHelper.ThrowArgumentNullException("targetBitmap");
            }
            if (frameBitmap == null)
            {
                ThrowHelper.ThrowArgumentNullException("frameBitmap");
            }

            // compute coordinates of centered & offset frame
            SizeI source = new SizeI(frameBitmap.PixelWidth, frameBitmap.PixelHeight);
            int   x = Fortran.NInt(target.X + offset.X + (target.Width - source.Width) / 2.0);
            int   y = Fortran.NInt(target.Y + offset.Y + (target.Height - source.Height) / 2.0);
            int   endX = x + source.Width, endY = y + source.Height;

            // adjust horizontal coordinates for desired scaling
            switch (scalingX)
            {
            case ImageScaling.Repeat:
                while (x > target.X)
                {
                    x -= source.Width;
                }
                endX = target.Right;
                break;

            case ImageScaling.Stretch:
                ThrowHelper.ThrowInvalidEnumArgumentException(
                    "scalingX", (int)scalingX, typeof(ImageScaling));
                break;
            }

            // adjust vertical coordinates for desired scaling
            switch (scalingY)
            {
            case ImageScaling.Repeat:
                while (y > target.Y)
                {
                    y -= source.Height;
                }
                endY = target.Bottom;
                break;

            case ImageScaling.Stretch:
                ThrowHelper.ThrowInvalidEnumArgumentException(
                    "scalingY", (int)scalingY, typeof(ImageScaling));
                break;
            }

            // check scaling vector for mirroring
            bool mirrorX = (scalingVector.X < 0);
            bool mirrorY = (scalingVector.Y < 0);

            // repeatedly copy frame to fill target bounds
            for (int dx = x; dx < endX; dx += source.Width)
            {
                for (int dy = y; dy < endY; dy += source.Height)
                {
                    // adjust for obscured upper & left edges
                    int sourceX = 0, sourceY = 0, targetX = 0, targetY = 0;
                    if (dx < 0)
                    {
                        sourceX = -dx;
                    }
                    else
                    {
                        targetX = dx;
                    }
                    if (dy < 0)
                    {
                        sourceY = -dy;
                    }
                    else
                    {
                        targetY = dy;
                    }

                    // adjust for obscured lower & right edges
                    int width  = Math.Min(source.Width - sourceX, target.Right - targetX);
                    int height = Math.Min(source.Height - sourceY, target.Bottom - targetY);
                    if (width <= 0 || height <= 0)
                    {
                        continue;
                    }

                    // call mirroring overload only if necessary
                    RectI bounds = new RectI(sourceX, sourceY, width, height);
                    if (mirrorX || mirrorY)
                    {
                        targetBitmap.Read(targetX, targetY, frameBitmap, bounds, mirrorX, mirrorY);
                    }
                    else
                    {
                        targetBitmap.Read(targetX, targetY, frameBitmap, bounds);
                    }
                }
            }
        }
Beispiel #14
0
        public static void Image(this IContainer parent, Stream fileStream, ImageScaling scaling = ImageScaling.FitWidth)
        {
            var image = SKImage.FromEncodedData(fileStream);

            parent.Image(image, scaling);
        }
Beispiel #15
0
        public static void Image(this IContainer parent, string filePath, ImageScaling scaling = ImageScaling.FitWidth)
        {
            var image = SKImage.FromEncodedData(filePath);

            parent.Image(image, scaling);
        }
Beispiel #16
0
        public static void Image(this IContainer parent, byte[] imageData, ImageScaling scaling = ImageScaling.FitWidth)
        {
            var image = SKImage.FromEncodedData(imageData);

            parent.Image(image, scaling);
        }