Beispiel #1
0
        public RectangleF getCustomCropSourceRect(SizeF imageSize)
        {
            double xunits = this.Get <double>("cropxunits", 0);
            double yunits = this.Get <double>("cropyunits", 0);

            return(PolygonMath.GetCroppingRectangle(CropValues, xunits, yunits, imageSize));
        }
Beispiel #2
0
        /// <summary>
        /// Draws a gradient around the specified polygon. Fades from 'inner' to 'outer' over a distance of 'width' pixels.
        /// </summary>
        /// <param name="g"></param>
        /// <param name="poly"></param>
        /// <param name="inner"></param>
        /// <param name="outer"></param>
        /// <param name="width"></param>
        public static void DrawOuterGradient(Graphics g, PointF[] poly, Color inner, Color outer, float width)
        {
            PointF[,] corners = PolygonMath.GetCorners(poly, width);
            PointF[,] sides   = PolygonMath.GetSides(poly, width);
            //Overlapping these causes darker areas... Don't use InflatePoly

            //Paint corners
            for (int i = 0; i <= corners.GetUpperBound(0); i++)
            {
                PointF[] pts = PolygonMath.GetSubArray(corners, i);
                using (Brush b = PolygonMath.GenerateRadialBrush(inner, outer, pts[0], width + 1)) {
                    g.FillPolygon(b, pts);
                }
            }
            //Paint sides
            for (int i = 0; i <= sides.GetUpperBound(0); i++)
            {
                PointF[] pts = PolygonMath.GetSubArray(sides, i);
                using (LinearGradientBrush b = new LinearGradientBrush(pts[3], pts[0], inner, outer)) {
                    b.SetSigmaBellShape(1);
                    b.WrapMode = WrapMode.TileFlipXY;
                    g.FillPolygon(b, pts);
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Detects features on a grayscale image.
        /// </summary>
        /// <param name="img"></param>
        /// <param name="storage"></param>
        /// <returns></returns>
        protected override List <Face> DetectFeatures(IplImage img, CvMemStorage storage)
        {
            //Determine minimum face size
            var minSize = (int)Math.Round((double)MinSizePercent / 100.0 * Math.Min(img.Width, img.Height));


            //Detect faces (frontal). TODO: side
            Stopwatch watch = Stopwatch.StartNew();

            CvAvgComp[] faces = Cv.HaarDetectObjects(img, Cascades["FaceCascade"], storage, 1.0850, MinConfidenceLevel, 0, new CvSize(minSize, minSize)).ToArrayAndDispose();
            watch.Stop();
            Debug.WriteLine("Face detection time = " + watch.ElapsedMilliseconds);

            //Sort by accuracy
            Array.Sort <CvAvgComp>(faces, CompareByNeighbors);

            //Convert into feature objects list
            List <Face> features = new List <Face>(faces.Length);

            foreach (CvAvgComp face in faces)
            {
                features.Add(new Face(PolygonMath.ScaleRect(face.Rect.ToRectangleF(), ExpandX, ExpandY), face.Neighbors));
            }

            //Unless we're below MinFaces, filter out the low confidence matches.
            while (features.Count > MinFaces && features[features.Count - 1].Accuracy < ConfidenceLevelThreshold)
            {
                features.RemoveAt(features.Count - 1);
            }


            //Never return more than [MaxFaces]
            return((features.Count > MaxFaces) ? features.GetRange(0, MaxFaces) : features);
        }
Beispiel #4
0
 /// <summary>
 /// When the player painted a polygon, check if this collectable is inside,
 /// collect and destroy it and tell it to the polygonpainter to handle it.
 /// </summary>
 /// <param name="e">E.</param>
 void polygonPainter_PolygonPainted(PolygonPainterEventArgs e)
 {
     if (PolygonMath.IntersectNew(e.Points, e.PointCount, transform.position))
     {
         DestroyMe();
     }
 }
Beispiel #5
0
        /// <summary>
        /// Translates and scales all rings and invisible polygons as specified.
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        public void Shift(RectangleF from, RectangleF to)
        {
            PointF fromOrigin = new PointF(from.X + (from.Width / 2), from.Y + (from.Height / 2));
            PointF toOrigin   = new PointF(to.X + (to.Width / 2), to.Y + (to.Height / 2));

            PointF offset = new PointF(toOrigin.X - fromOrigin.X, toOrigin.Y - fromOrigin.Y);
            double xd     = to.Width / from.Width;
            double yd     = to.Height / from.Height;

            //Offset points
            foreach (PointSet ps in ringList)
            {
                if (ps.flags != PointFlags.Ignored)
                {
                    ps.points = PolygonMath.MovePoly(ps.points, offset);
                }
            }
            //Scale points
            foreach (PointSet ps in ringList)
            {
                if (ps.flags != PointFlags.Ignored)
                {
                    ps.points = PolygonMath.ScalePoints(ps.points, xd, yd, toOrigin);
                }
            }
        }
Beispiel #6
0
        /// <summary>
        /// Translates the specified points from the source bitmap coordinate space to the destination image coordinate space.
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public PointF[] TranslatePoints(PointF[] points, ImageState s)
        {
            PointF[] moved     = new PointF[points.Length];
            PointF[] dest      = s.layout["image"];
            double   newWidth  = PolygonMath.Dist(dest[0], dest[1]);
            double   newHeight = PolygonMath.Dist(dest[1], dest[2]);
            double   xScale    = newWidth / s.copyRect.Width;
            double   yScale    = newHeight / s.copyRect.Height;

            double angle = GetAngleFromXAxis(dest[0], dest[1]);

            for (int i = 0; i < points.Length; i++)
            {
                PointF p = points[i];
                p.X     -= s.copyRect.X;                                //Translate
                p.Y     -= s.copyRect.Y;
                p.X      = (float)(p.X * xScale);                       //Scale
                p.Y      = (float)(p.Y * yScale);
                p        = PolygonMath.RotateVector(p, angle, dest[0]); //Rotate
                p.X     += dest[0].X;
                p.Y     += dest[1].Y;
                moved[i] = p;
            }
            return(moved);
        }
Beispiel #7
0
        protected override RequestedAction RenderEffects(ImageState s)
        {
            if (base.RenderEffects(s) == RequestedAction.Cancel)
            {
                return(RequestedAction.Cancel);                                                 //Call extensions
            }
            if (s.destGraphics == null)
            {
                return(RequestedAction.None);
            }

            //parse shadow
            Color shadowColor = ParseUtils.ParseColor(s.settings["shadowColor"], Color.Transparent);
            int   shadowWidth = s.settings.Get <int>("shadowWidth", -1);

            //Skip on transparent or 0-width shadow
            if (shadowColor == Color.Transparent || shadowWidth <= 0)
            {
                return(RequestedAction.None);
            }

            using (Brush b = new SolidBrush(shadowColor)) {
                //Offsets may show inside the shadow - so we have to fix that
                s.destGraphics.FillPolygon(b,
                                           PolygonMath.InflatePoly(s.layout["shadowInner"], 1)); //Inflate 1 for FillPolygon rounding errors.
            }
            //Then we can draw the outer gradient
            DrawOuterGradient(s.destGraphics, s.layout["shadowInner"],
                              shadowColor, Color.Transparent, shadowWidth);

            return(RequestedAction.None);
        }
Beispiel #8
0
        /// <summary>
        /// Returns a rectangle representing the given ring - if it is an axis-parallel rectangle. Otherwise returns null;
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public RectangleF?GetRingAsRectF(string name)
        {
            var poly  = this[name];
            var rect  = PolygonMath.GetBoundingBox(poly);
            var poly2 = PolygonMath.ToPoly(rect);

            return(PolygonMath.ArraysEqual(poly, poly2) ? (RectangleF?)rect : null);
        }
Beispiel #9
0
 /// <summary>
 /// Rotates all existing rings (Except those flagged ignore)
 /// </summary>
 /// <param name="degrees"></param>
 /// <param name="origin"></param>
 /// <returns></returns>
 public void Rotate(double degrees, PointF origin)
 {
     foreach (PointSet ps in ringList)
     {
         if (ps.flags != PointFlags.Ignored)
         {
             ps.points = PolygonMath.RotatePoly(ps.points, degrees, origin);
         }
     }
 }
Beispiel #10
0
 public void Round()
 {
     foreach (PointSet ps in ringList)
     {
         if (ps.flags != PointFlags.Ignored)
         {
             ps.points = PolygonMath.RoundPoints(ps.points);
         }
     }
 }
Beispiel #11
0
 /// <summary>
 /// Scales all rings and invisible polygons by the specified factor, around the specified point.
 /// </summary>
 /// <param name="factor"></param>
 /// <param name="origin"></param>
 public void Scale(double factor, PointF origin)
 {
     foreach (PointSet ps in ringList)
     {
         if (ps.flags != PointFlags.Ignored)
         {
             ps.points = PolygonMath.ScalePoints(ps.points, factor, factor, origin);
         }
     }
 }
Beispiel #12
0
 public static Coord?HitTestPolygon(PointT point, Coord radius, IEnumerable <PointT> points, IList <int> divisions, out PointT projected)
 {
     if (PolygonMath.GetWindingNumber(points, point) != 0)
     {
         projected = point;
         return(-1);
     }
     else
     {
         return(HitTestPolyline(point, radius, points, divisions, out projected));
     }
 }
Beispiel #13
0
        /// <summary>
        /// Gets a bounding box that encloses all rings that don't have ExcludeFromBoundingBox set.
        /// </summary>
        /// <returns></returns>
        public RectangleF GetBoundingBox()
        {
            List <PointF> points = new List <PointF>(ring.Count * 5);

            foreach (PointSet val in ringList)
            {
                if (val.flags == PointFlags.Ring)
                {
                    points.AddRange(val.points);
                }
            }
            return(PolygonMath.GetBoundingBox(points.ToArray()));
        }
Beispiel #14
0
 public override object HitTest(PointT point, Coord radius, out PointT projected)
 {
     projected = point;
     if (!BBox.Inflated(radius, radius).Contains(point))
     {
         return(null);
     }
     if (PolygonMath.GetWindingNumber(Points, point) != 0)
     {
         projected = point;
         return(-1);
     }
     return(HitTestPolyline(point, radius, Points, Divisions, out projected));
 }
Beispiel #15
0
        /// <summary>
        /// Copies layout information from the given image state to the current instance.
        /// Does not populate message or 'features' variables
        /// </summary>
        /// <param name="s"></param>
        public void PopulateFrom(ImageState s)
        {
            this.ow    = s.originalSize.Width;
            this.oh    = s.originalSize.Height;
            this.cropx = s.copyRect.X;
            this.cropy = s.copyRect.Y;
            this.cropw = s.copyRect.Width;
            this.croph = s.copyRect.Height;
            RectangleF dest = PolygonMath.GetBoundingBox(s.layout["image"]);

            this.dx = dest.X;
            this.dy = dest.Y;
            this.dw = dest.Width;
            this.dh = dest.Height;
        }
Beispiel #16
0
        RectangleF GetZoomCrop()
        {
            RectangleF box = SalientBoundingBox();

            var bounds = new RectangleF(new PointF(0, 0), ImageSize);

            //Clip box to original image bounds
            box = PolygonMath.ClipRectangle(box, bounds);

            //Crop close
            var copySize = PolygonMath.ScaleOutside(box.Size, TargetSize);

            //Clip to bounds.
            return(PolygonMath.ClipRectangle(PolygonMath.ExpandTo(box, copySize), bounds));
        }
Beispiel #17
0
        /// <summary>
        /// Normalizes all rings and invisible polygons so that the outermost ring's bounding box starts at the specified orign.
        /// </summary>
        /// <param name="origin"></param>
        /// <returns></returns>
        public void Normalize(PointF origin)
        {
            PointF offset = GetBoundingBox().Location;

            offset.X *= -1;
            offset.Y *= -1;

            foreach (PointSet ps in ringList)
            {
                if (ps.flags != PointFlags.Ignored)
                {
                    ps.points = PolygonMath.MovePoly(ps.points, offset);
                }
            }
        }
Beispiel #18
0
        protected override RequestedAction RenderOverlays(ImageState s)
        {
            IEnumerable <Overlay> overlays = RequestOverlays;

            if (overlays == null)
            {
                return(RequestedAction.None);                  //skip town if there's no overlay object cached.
            }
            Graphics g = s.destGraphics;

            g.SmoothingMode      = this.Smoothing;
            g.CompositingMode    = CompositingMode.SourceOver;
            g.CompositingQuality = this.Compositing;
            g.InterpolationMode  = InterpolationMode.HighQualityBicubic;
            g.PixelOffsetMode    = PixelOffsetMode.HighQuality;

            foreach (Overlay o in overlays)
            {
                if (string.IsNullOrEmpty(o.OverlayPath))
                {
                    continue;                                     //Skip overlays without a path
                }
                try {
                    //Get the memcached overlay (assuming SourceFileCache is installed)
                    using (Bitmap b = c.CurrentImageBuilder.LoadImage(o.OverlayPath, new ResizeSettings("memcache=true")))
                        using (ImageAttributes ia = new ImageAttributes()) {
                            ia.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY);

                            g.DrawImage(b, PolygonMath.getParallelogram(new LayoutEngine().GetOverlayParalellogram(o, b.Size, s)), new Rectangle(0, 0, b.Width, b.Height), GraphicsUnit.Pixel, ia);
                            //Draw the poly if requested.
                            if (Utils.getBool(s.settings, "customoverlay.showpoly", false))
                            {
                                g.DrawPolygon(Pens.Green, new LayoutEngine().TranslatePoints(o.Poly, s));
                            }
                        }
                } catch (FileNotFoundException fe) {
                    if (!IgnoreMissingFiles)
                    {
                        throw new ImageMissingException("The overlay image " + o.OverlayPath + " could not be found.");
                    }
                }
            }

            return(RequestedAction.None);
        }
Beispiel #19
0
        protected override RequestedAction PreFlushChanges(ImageState s)
        {
            if (s.destGraphics == null)
            {
                return(RequestedAction.None);
            }


            Interlocked.Increment(ref requestCount); //Track request count

            string             mode = c.get("trial.watermarkMode", "After500");
            TrialWatermarkMode m    = TrialWatermarkMode.After500;

            if ("always".Equals(mode, StringComparison.OrdinalIgnoreCase))
            {
                m = TrialWatermarkMode.Always;
            }
            if ("randomly".Equals(mode, StringComparison.OrdinalIgnoreCase))
            {
                m = TrialWatermarkMode.Randomly;
            }

            bool applyWatermark = (m == TrialWatermarkMode.Always);

            if (m == TrialWatermarkMode.After500 && requestCount > 500)
            {
                applyWatermark = true;
            }
            if (m == TrialWatermarkMode.Randomly)
            {
                applyWatermark = (new Random(requestCount).Next(0, 41) < 10);                                   //25% chance
            }
            if (!applyWatermark)
            {
                return(RequestedAction.None);
            }

            DrawString(PolygonMath.GetBoundingBox(s.layout["image"]), s.destGraphics, "Unlicensed", FontFamily.GenericSansSerif, Color.FromArgb(70, Color.White));



            return(RequestedAction.None);
        }
Beispiel #20
0
        protected override RequestedAction LayoutEffects(ImageState s)
        {
            float shadowWidth = s.settings.Get <float>("shadowWidth", 0);

            if (shadowWidth != 0)
            {
                var    offset       = s.settings.GetList <float>("shadowOffset", 0, 2);
                PointF shadowOffset = offset == null ? new PointF(0, 0) : new PointF(offset[0], offset[1]);

                //Clone last ring, then offset it - provides the inner bounds of the shadow later
                s.layout.AddInvisiblePolygon("shadowInner", PolygonMath.MovePoly(s.layout.LastRing.points, shadowOffset));

                //Determine the outer bound of the shadow
                s.layout.AddRing("shadow", PolygonMath.InflatePoly(s.layout.LastRing.points, new float[] {
                    Math.Max(0, shadowWidth - shadowOffset.Y),
                    Math.Max(0, shadowWidth + shadowOffset.X),
                    Math.Max(0, shadowWidth + shadowOffset.Y),
                    Math.Max(0, shadowWidth - shadowOffset.X)
                }));
            }
            return(RequestedAction.None);
        }
Beispiel #21
0
        public PolyRect(PointF[] points)
        {
            var rect = PolygonMath.GetBoundingBox(points);

            this.SetBounds(rect.X, rect.Y, rect.Width, rect.Height);

            // PolygonMath.IsUnrotated can tell us that the points follow a
            // particular pattern, but in order to represent 90-degree rotations
            // and flips, we want to consider them to be *non-rectangle* points.
            // Therefore, we use a much more strict definition: there can only
            // be 4 points, and they must be in the canonical order.
            var right  = this.x + this.width;
            var bottom = this.y + this.height;

            this.rect = points.Length == 4 &&
                        points[0].X == this.x && points[0].Y == this.y &&
                        points[1].X == right && points[1].Y == this.y &&
                        points[2].X == right && points[2].Y == bottom &&
                        points[3].X == this.x && points[3].Y == bottom;

            this.points = points.Select(p => new float[] { p.X, p.Y }).ToArray();
        }
Beispiel #22
0
        // To recognize a scribble we require the simplified line to reverse
        // direction at least three times. There are separate criteria for
        // erasing a shape currently being drawn and for erasing existing
        // shapes.
        //
        // The key difference between an "erase scribble" and a "cancel
        // scribble" is that an erase scribble starts out as such, while
        // a cancel scribble indicates that the user changed his mind, so
        // the line will not appear to be a scribble at the beginning.
        // The difference is detected by timestamps. For example, the
        // following diagram represents an "erase" operation and a "cancel"
        // operation. Assume the input points are evenly spaced in time,
        // and that the dots represent points where the input reversed
        // direction.
        //
        // Input points         ..........................
        // Reversals (erase)      .  .  .  .     .     .
        // Reversals (cancel)              .   .   .   .
        //
        // So, a scribble is considered an erasure if it satisfies t0 < t1,
        // where t0 is the time between mouse-down and the first reversal,
        // and t1 is the time between the first and third reversals. A cancel
        // operation satisfies t0 > t1 instead.
        //
        // Both kinds of scribble need to satisfy the formula LL*c > CHA,
        // where c is a constant factor in pixels, LL is the drawn line
        // length and CHA is the area of the Convex Hull that outlines the
        // drawn figure. This formula basically detects that the user
        // is convering the same ground repeatedly; if the pen reverses
        // direction repeatedly but goes to new places each time, it's not
        // considered an erasure scribble. For a cancel scribble, LL is
        // computed starting from the first reversal.
        IEnumerable <Shape> RecognizeScribbleForEraseOrCancel(DragState state, out bool cancel, out List <PointT> simplifiedSS)
        {
            cancel       = false;
            simplifiedSS = null;
            var tolerance    = state._inputTransform.Transform(new VectorT(0, 10)).Length();
            var simplifiedMP = LineMath.SimplifyPolyline(
                state.UnfilteredMousePoints.Select(p => p.Point), tolerance);
            List <int> reversals = FindReversals(simplifiedMP, 3);

            if (reversals.Count >= 3)
            {
                simplifiedSS = simplifiedMP.Select(p => state._inputTransform.Transform(p)).ToList();
                // 3 reversals confirmed. Now decide: erase or cancel?
                int[] timeStampsMs = FindTimeStamps(state.UnfilteredMousePoints, simplifiedMP);
                int   t0 = timeStampsMs[reversals[0]], t1 = timeStampsMs[reversals[2]] - t0;
                cancel = t0 > t1 + 500;

                // Now test the formula LL*c > CHA as explained above
                IListSource <PointT> simplifiedMP_ = cancel ? simplifiedMP.Slice(reversals[0]) : simplifiedMP.AsListSource();
                float LL   = simplifiedMP_.AdjacentPairs().Sum(pair => pair.A.Sub(pair.B).Length());
                var   hull = PointMath.ComputeConvexHull(simplifiedMP);
                float CHA  = PolygonMath.PolygonArea(hull);
                if (LL * EraseNubWidth > CHA)
                {
                    // Erasure confirmed.
                    if (cancel)
                    {
                        return(EmptyList <Shape> .Value);
                    }

                    // Figure out which shapes to erase. To do this, we compute for
                    // each shape the amount of the scribble that overlaps that shape.
                    var simplifiedSS_ = simplifiedSS;
                    return(_doc.Shapes.Where(s => ShouldErase(s, simplifiedSS_)).ToList());
                }
            }
            return(null);
        }
Beispiel #23
0
        private RectangleF determineManualCropWindow(ResizeSettings settings)
        {
            RectangleF cropWindow = copyRect;

            if (cropWindow.IsEmpty)
            {
                //Use the crop size if present.
                cropWindow = new RectangleF(new PointF(0, 0), originalSize);
                if (settings.GetList <double>("crop", 0, 4) != null)
                {
                    cropWindow = PolygonMath.ToRectangle(settings.getCustomCropSourceRect(originalSize)); //Round the custom crop rectangle coordinates
                    //Ensure right/bottom bounded after rounding completes
                    cropWindow.Width  = Math.Min(originalSize.Width - cropWindow.Left, cropWindow.Width);
                    cropWindow.Height = Math.Min(originalSize.Height - cropWindow.Top, cropWindow.Height);

                    if (cropWindow.Size.IsEmpty)
                    {
                        throw new Exception("You must specify a custom crop rectangle if crop=custom");
                    }
                }
            }
            return(cropWindow);
        }
Beispiel #24
0
        /// <summary>Adds a point to DragState with a filtering algorithm. The
        /// default filtering algorithm supports "backing up the mouse" for erasure.</summary>
        /// <returns>true if a point was added, false if not.</returns>
        protected virtual bool AddFiltered(DragState state, DragPoint dp)
        {
            var points = state.MousePoints;

            if (points.Count < 2)
            {
                return(AddIfFarEnough(points, dp));
            }

            var newSeg = (LineSegment <float>)points.Last.Point.To(dp.Point);
            // Strategy:
            // 1. Stroke the new segment with a simple rectangle with no endcap.
            //    The rectangle will be a thin box around the point (halfwidth
            //    is 1..2)
            var newRect   = SimpleStroke(newSeg, EraseThreshold1);
            var newRectBB = newRect.ToBoundingBox();

            // 2. Identify the most recent intersection point between this rectangle
            //    (newRect) and the line being drawn. (if there is no such point,
            //    there is no erasure. Done.)
            // 2b. That intersection point is the one _entering_ the rectangle. Find
            //    the previous intersection point, the one that exits the rectangle.
            //    this is the beginning of the region to potentially erase.
            var           older       = points.Reverse().AdjacentPairs().Select(pair => pair.B.Point.To(pair.A.Point));
            Point <float> beginning   = default(Point <float>);
            bool          keepLooking = false;
            int           offs        = 0;
            var           e           = older.GetEnumerator();

            for (; e.MoveNext(); offs++)
            {
                var seg  = e.Current;
                var list = FindIntersectionsWith(seg, newRect, true).ToList();
                if (list.Count != 0)
                {
                    var min = list.MinOrDefault(p => p.A);
                    beginning = min.B;
                    if (!(offs == 0 && min.A == 1))
                    {
                        if (keepLooking || !PolygonMath.IsPointInPolygon(newRect, seg.A))
                        {
                            break;
                        }
                        keepLooking = true;
                    }
                }
                else if (offs == 0)
                {
                }                                       // todo: use IsPointInPolygon if itscs unstable
            }

            int iFirst = points.Count - 1 - offs;             // index of the first point inside the region (iFirst-1 is outside)

            if (iFirst > 0)
            {
                // 3. Between here and there, identify the farthest point away from the
                //    new point (dp.Point).
                var region       = ((IList <DragPoint>)points).Slice(iFirst);
                int offsFarthest = region.IndexOfMax(p => (p.Point.Sub(dp.Point)).Quadrance());
                int iFarthest    = iFirst + offsFarthest;
                // 4. Make sure that all the points between here and there are close to
                //    this line (within, say... 8 pixels). If so, we have erasure.
                var seg = dp.Point.To(points[iFarthest].Point);
                if (region.All(p => p.Point.DistanceTo(seg) < EraseThreshold2))
                {
                    // 5. Respond to erasure by deleting all the points between there
                    //    and here, not including the first or last point.
                    // 5b. Consider adding the intersection point found in step 2b to
                    //    the point list, before adding the new point.
                    points.Resize(iFirst);
                    if (points.Count == 0 || (points.Last.Point.Sub(beginning)).Length() >= MinDistBetweenDragPoints)
                    {
                        points.Add(new DragPoint(beginning, 10, points));
                    }
                }
            }

            return(AddIfFarEnough(points, dp));
        }
        private Size GetOutputSize(ResizeSettings settings, double boundingWidth, double boundingHeight)
        {
            // Output size is determined by resize settings, if available.
            //  maxwidth, maxheight
            //      – Fit the image within the specified bounds, preserving aspect ratio.
            //  width, height
            //      – Force the final width and/or height to certain dimensions.
            //        Whitespace will be added if the aspect ratio is different.
            // This plugin renders to a size within the requested size and then expects remaining plugins in the
            // pipeline to perform and additional processing such as adding whitespace, etc.
            // It can safely treat width/height as maxwidth/maxheight.

            double imageRatio = boundingWidth / boundingHeight;
            double width      = settings.Width;
            double height     = settings.Height;
            double maxwidth   = settings.MaxWidth;
            double maxheight  = settings.MaxHeight;

            //Allow overrides with pdfwidth and pdfheight when we *want* to rescale afterwards.
            int pw = settings.Get("pdfwidth", -1);
            int ph = settings.Get("pdfheight", -1);

            if (pw > 0)
            {
                width = pw; maxwidth = -1;
            }
            if (ph > 0)
            {
                height = ph; maxheight = -1;
            }

            //Handle cases of width/maxheight and height/maxwidth as in legacy versions.
            if (width != -1 && maxheight != -1)
            {
                maxheight = Math.Min(maxheight, (width / imageRatio));
            }
            if (height != -1 && maxwidth != -1)
            {
                maxwidth = Math.Min(maxwidth, (height * imageRatio));
            }

            //Eliminate cases where both a value and a max value are specified: use the smaller value for the width/height
            if (maxwidth > 0 && width > 0)
            {
                width = Math.Min(maxwidth, width); maxwidth = -1;
            }
            if (maxheight > 0 && height > 0)
            {
                height = Math.Min(maxheight, height); maxheight = -1;
            }

            //Move values to width/height
            if (width <= 0)
            {
                width = maxwidth;
            }
            if (height <= 0)
            {
                height = maxheight;
            }

            //Calculate missing value(s)
            if (width > 0 && height <= 0)
            {
                height = width / imageRatio;
            }
            else if (height > 0 && width <= 0)
            {
                width = height * imageRatio;
            }
            else if (width <= 0 && height <= 0)
            { // If neither width nor height as specified use default values
                width  = DefaultWidth;
                height = DefaultHeight;
            }

            // Limit maximum output size
            width  = Math.Min(width, this.MaxWidth);
            height = Math.Min(height, this.MaxHeight);


            // Determine the scaling values, and use the smallest to ensure we fit in the bounding box without changing
            // the aspect ratio otherwise we will crop.
            //Use a scaled version of boundingBox inside our maximum width and height constraints.
            return(PolygonMath.RoundPoints(PolygonMath.ScaleInside(new SizeF((float)boundingWidth, (float)boundingHeight), new SizeF((float)width, (float)height))));
        }
Beispiel #26
0
        protected override RequestedAction RenderImage(ImageState s)
        {
            //Skip this when we are doing simulations
            if (s.destGraphics == null)
            {
                return(RequestedAction.None);
            }

            //If there's pre-rendering involved this optimization is utterly pointless.
            if (s.preRenderBitmap != null)
            {
                return(RequestedAction.None);
            }

            //Find out what the speed setting is.
            int speed = 0;

            if (string.IsNullOrEmpty(s.settings["speed"]) || !int.TryParse(s.settings["speed"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out speed))
            {
                speed = 0;
            }

            if (speed < 1)
            {
                return(RequestedAction.None);
            }

            s.destGraphics.CompositingMode    = CompositingMode.SourceCopy;
            s.destGraphics.CompositingQuality = CompositingQuality.HighSpeed;
            if (speed == 1)
            {
                s.destGraphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
            }
            else
            {
                s.destGraphics.InterpolationMode = InterpolationMode.Bilinear;
            }

            s.destGraphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
            s.destGraphics.SmoothingMode   = SmoothingMode.HighSpeed;

            s.copyAttibutes.SetWrapMode(WrapMode.TileFlipXY);

            if (speed < 3)
            {
                s.destGraphics.DrawImage(s.sourceBitmap, PolygonMath.getParallelogram(s.layout["image"]), s.copyRect, GraphicsUnit.Pixel, s.copyAttibutes);
            }
            else if (speed < 4)
            {
                Rectangle midsize = PolygonMath.ToRectangle(PolygonMath.GetBoundingBox(s.layout["image"]));

                using (Image thumb = s.sourceBitmap.GetThumbnailImage(midsize.Width, midsize.Height, delegate() { return(false); }, IntPtr.Zero)) {
                    double     xfactor  = (double)thumb.Width / (double)s.sourceBitmap.Width;
                    double     yfactor  = (double)thumb.Height / (double)s.sourceBitmap.Height;
                    RectangleF copyPart = new RectangleF((float)(s.copyRect.Left * xfactor),
                                                         (float)(s.copyRect.Top * yfactor),
                                                         (float)(s.copyRect.Width * xfactor),
                                                         (float)(s.copyRect.Height * yfactor));
                    if (Math.Floor(copyPart.Height) == thumb.Height || Math.Ceiling(copyPart.Height) == thumb.Height)
                    {
                        copyPart.Height = thumb.Height;
                    }
                    if (Math.Floor(copyPart.Width) == thumb.Width || Math.Ceiling(copyPart.Width) == thumb.Width)
                    {
                        copyPart.Width = thumb.Width;
                    }
                    s.destGraphics.DrawImage(thumb, PolygonMath.getParallelogram(s.layout["image"]), copyPart, GraphicsUnit.Pixel, s.copyAttibutes);
                }
            }
            else
            {
                RectangleF box = PolygonMath.GetBoundingBox(PolygonMath.getParallelogram(s.layout["image"]));
                s.destGraphics.CompositingMode = CompositingMode.SourceCopy;
                s.destGraphics.DrawImage(s.sourceBitmap, box.Left, box.Top, box.Width, box.Height);
            }

            return(RequestedAction.Cancel);
        }
Beispiel #27
0
 /// <summary>
 /// Inflates the last ring using the specified padding options. Returns the resulting ring object
 /// </summary>
 /// <param name="name"></param>
 /// <param name="padding"></param>
 /// <returns></returns>
 public PointSet AddRing(string name, BoxPadding padding)
 {
     return(AddRing(name, PolygonMath.InflatePoly(LastRing.points, padding.GetEdgeOffsets())));
 }
Beispiel #28
0
        protected override RequestedAction LayoutImage(ImageState s)
        {
            //Only activated if both width and height are specified, and mode=crop.
            if (s.settings.Mode != FitMode.Crop || s.settings.Width < 0 || s.settings.Height < 0)
            {
                return(RequestedAction.None);
            }

            //Calculate bounding box for all coordinates specified.
            double[] focus = NameValueCollectionExtensions.GetList <double>(s.settings, "c.focus", null, 2, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72);
            if (focus == null)
            {
                return(RequestedAction.None);
            }
            RectangleF box = PolygonMath.GetBoundingBox(focus);

            var bounds = new RectangleF(new PointF(0, 0), s.originalSize);

            //Clip box to original image bounds
            box = PolygonMath.ClipRectangle(box, bounds);

            var targetSize = new SizeF(s.settings.Width, s.settings.Height);

            SizeF copySize;

            //Now, we can either crop as closely as possible or as loosely as possible.
            if (NameValueCollectionExtensions.Get <bool>(s.settings, "c.zoom", false) && box.Width > 0 && box.Height > 0)
            {
                //Crop close
                copySize = PolygonMath.ScaleOutside(box.Size, targetSize);
            }
            else
            {
                //Crop minimally
                copySize = PolygonMath.ScaleInside(targetSize, bounds.Size);
                //Ensure it's outside the box
                if (!PolygonMath.FitsInside(box.Size, copySize))
                {
                    copySize = PolygonMath.ScaleOutside(box.Size, copySize);
                }
            }
            //Clip to bounds.
            box = PolygonMath.ClipRectangle(PolygonMath.ExpandTo(box, copySize), bounds);

            s.copyRect = box;

            ///What is the vertical and horizontal aspect ratio different in result pixels?
            var padding = PolygonMath.ScaleInside(box.Size, targetSize);

            padding = new SizeF(targetSize.Width - padding.Width, targetSize.Height - padding.Height);


            //So, if we haven't met the aspect ratio yet, what mode will we pass on?
            var finalmode = NameValueCollectionExtensions.Get <FitMode>(s.settings, "c.finalmode", FitMode.Pad);

            //Crop off 1 or 2 pixels instead of padding without worrying too much
            if (finalmode == FitMode.Pad && padding.Width + padding.Height < 3)
            {
                finalmode = FitMode.Crop;
            }

            s.settings.Mode = finalmode;

            return(RequestedAction.None);
        }
Beispiel #29
0
        protected override RequestedAction PreRenderImage(ImageState s)
        {
            //Skip this when we are doing simulations
            if (s.destGraphics == null)
            {
                return(RequestedAction.None);
            }

            string sf = s.settings["fi.scale"];

            if (string.IsNullOrEmpty(sf))
            {
                return(RequestedAction.None);
            }
            bool validAlg            = false;
            FREE_IMAGE_FILTER filter = ParseResizeAlgorithm(sf, FREE_IMAGE_FILTER.FILTER_CATMULLROM, out validAlg);

            if (!validAlg)
            {
                throw new ImageProcessingException("The specified resizing filter '" + sf + "' did not match bicubic, bilinear, box, bspline, catmullrom, or lanczos.");
            }

            //Set copy attributes
            s.copyAttibutes.SetWrapMode(WrapMode.TileFlipXY);


            //The minimum dimensions of the temporary bitmap.
            SizeF targetSize = PolygonMath.getParallelogramSize(s.layout["image"]);

            targetSize = new SizeF((float)Math.Ceiling(targetSize.Width), (float)Math.Ceiling(targetSize.Height));

            s.ApplyCropping();
            s.EnsurePreRenderBitmap();

            //The size of the temporary bitmap.
            //We want it larger than the size we'll use on the final copy, so we never upscale it
            //- but we also want it as small as possible so processing is fast.
            SizeF    tempSize   = PolygonMath.ScaleOutside(targetSize, s.copyRect.Size);
            int      tempWidth  = (int)Math.Ceiling(tempSize.Width);
            int      tempHeight = (int)Math.Ceiling(tempSize.Height);
            FIBITMAP src        = FIBITMAP.Zero;
            FIBITMAP midway     = FIBITMAP.Zero;

            try {
                var oldbit = s.preRenderBitmap ?? s.sourceBitmap;
                //Crop if needed, Convert, scale, then convert back.
                src = FreeImage.CreateFromBitmap(oldbit);

                midway = FreeImage.Rescale(src, tempWidth, tempHeight, filter);
                FreeImage.UnloadEx(ref src);
                //Clear the old pre-rendered image if needed
                if (s.preRenderBitmap != null)
                {
                    s.preRenderBitmap.Dispose();
                }
                //Reassign the pre-rendered image
                s.preRenderBitmap = FreeImage.GetBitmap(midway);
                s.copyRect        = new RectangleF(0, 0, s.preRenderBitmap.Width, s.preRenderBitmap.Height);
                FreeImage.UnloadEx(ref midway);
                s.preRenderBitmap.MakeTransparent();
            } finally {
                if (!src.IsNull)
                {
                    FreeImage.UnloadEx(ref src);
                }
                if (!midway.IsNull)
                {
                    FreeImage.UnloadEx(ref midway);
                }
            }


            return(RequestedAction.Cancel);
        }
Beispiel #30
0
        public void ApplySettings(ResizeSettings settings)
        {
            copyRect = determineManualCropWindow(settings);

            //Save the manual crop size.
            SizeF      manualCropSize = copyRect.Size;
            RectangleF manualCropRect = copyRect;

            FitMode fit = determineFitMode(settings);

            //Aspect ratio of the image
            double imageRatio = copyRect.Width / copyRect.Height;

            //Zoom factor
            double zoom = settings.Get <double>("zoom", 1);

            //The target size for the image
            targetSize = new SizeF(-1, -1);
            //Target area for the image
            areaSize = new SizeF(-1, -1);
            //If any dimensions are specified, calculate. Otherwise, use original image dimensions
            if (settings.Width != -1 || settings.Height != -1 || settings.MaxHeight != -1 || settings.MaxWidth != -1)
            {
                //A dimension was specified.
                //We first calculate the largest size the image can be under the width/height/maxwidth/maxheight restriction
                //- pretending stretch=fill and scale=both

                //Temp vars - results stored in targetSize and areaSize
                double width     = settings.Width;
                double height    = settings.Height;
                double maxwidth  = settings.MaxWidth;
                double maxheight = settings.MaxHeight;

                //Eliminate cases where both a value and a max value are specified: use the smaller value for the width/height
                if (maxwidth > 0 && width > 0)
                {
                    width = Math.Min(maxwidth, width); maxwidth = -1;
                }
                if (maxheight > 0 && height > 0)
                {
                    height = Math.Min(maxheight, height); maxheight = -1;
                }

                //Handle cases of width/maxheight and height/maxwidth as in legacy version
                if (width != -1 && maxheight != -1)
                {
                    maxheight = Math.Min(maxheight, (width / imageRatio));
                }
                if (height != -1 && maxwidth != -1)
                {
                    maxwidth = Math.Min(maxwidth, (height * imageRatio));
                }


                //Move max values to width/height. FitMode should already reflect the mode we are using, and we've already resolved mixed modes above.
                width  = Math.Max(width, maxwidth);
                height = Math.Max(height, maxheight);

                //Calculate missing value (a missing value is handled the same everywhere).
                if (width > 0 && height <= 0)
                {
                    height = width / imageRatio;
                }
                else if (height > 0 && width <= 0)
                {
                    width = height * imageRatio;
                }

                //We now have width & height, our target size. It will only be a different aspect ratio from the image if both 'width' and 'height' are specified.

                //FitMode.Max
                if (fit == FitMode.Max)
                {
                    areaSize = targetSize = PolygonMath.ScaleInside(manualCropSize, new SizeF((float)width, (float)height));
                    //FitMode.Pad
                }
                else if (fit == FitMode.Pad)
                {
                    areaSize   = new SizeF((float)width, (float)height);
                    targetSize = PolygonMath.ScaleInside(manualCropSize, areaSize);
                    //FitMode.crop
                }
                else if (fit == FitMode.Crop)
                {
                    //We autocrop - so both target and area match the requested size
                    areaSize = targetSize = new SizeF((float)width, (float)height);
                    RectangleF copyRect;

                    ScaleMode scale             = settings.Scale;
                    bool      cropWidthSmaller  = manualCropSize.Width <= (float)width;
                    bool      cropHeightSmaller = manualCropSize.Height <= (float)height;

                    //TODO: consider mode=crop;fit=upscale

                    // With both DownscaleOnly (where only one dimension is smaller than
                    // requested) and UpscaleCanvas, we will have a targetSize based on the
                    // minWidth & minHeight.
                    // TODO: what happens if mode=crop;scale=down but the target is larger than the source?

                    if ((scale == ScaleMode.DownscaleOnly && (cropWidthSmaller != cropHeightSmaller)) ||
                        (scale == ScaleMode.UpscaleCanvas && (cropHeightSmaller || cropWidthSmaller)))
                    {
                        var minWidth  = Math.Min(manualCropSize.Width, (float)width);
                        var minHeight = Math.Min(manualCropSize.Height, (float)height);

                        targetSize = new SizeF(minWidth, minHeight);

                        copyRect = manualCropRect = new RectangleF(0, 0, minWidth, minHeight);

                        // For DownscaleOnly, the areaSize is adjusted to the new targetSize as well.
                        if (scale == ScaleMode.DownscaleOnly)
                        {
                            areaSize = targetSize;
                        }
                    }
                    else
                    {
                        //Determine the size of the area we are copying
                        Size sourceSize = PolygonMath.RoundPoints(PolygonMath.ScaleInside(areaSize, manualCropSize));
                        //Center the portion we are copying within the manualCropSize
                        copyRect = new RectangleF(0, 0, sourceSize.Width, sourceSize.Height);
                    }


                    // Align the actual source-copy rectangle inside the available
                    // space based on the anchor.
                    this.copyRect = PolygonMath.ToRectangle(PolygonMath.AlignWith(copyRect, this.copyRect, settings.Anchor));
                }
                else
                { //Stretch and carve both act like stretching, so do that:
                    areaSize = targetSize = new SizeF((float)width, (float)height);
                }
            }
            else
            {
                //No dimensions specified, no fit mode needed. Use manual crop dimensions
                areaSize = targetSize = manualCropSize;
            }

            //Multiply both areaSize and targetSize by zoom.
            areaSize.Width    *= (float)zoom;
            areaSize.Height   *= (float)zoom;
            targetSize.Width  *= (float)zoom;
            targetSize.Height *= (float)zoom;

            //Todo: automatic crop is permitted to break the scaling rule Fix!!

            //Now do upscale/downscale check If they take effect, set targetSize to imageSize
            if (settings.Scale == ScaleMode.DownscaleOnly)
            {
                if (PolygonMath.FitsInside(manualCropSize, targetSize))
                {
                    //The image is smaller or equal to its target polygon. Use original image coordinates instead.
                    areaSize = targetSize = manualCropSize;
                    copyRect = manualCropRect;
                }
            }
            else if (settings.Scale == ScaleMode.UpscaleOnly)
            {
                if (!PolygonMath.FitsInside(manualCropSize, targetSize))
                {
                    //The image is larger than its target. Use original image coordinates instead
                    areaSize = targetSize = manualCropSize;
                    copyRect = manualCropRect;
                }
            }
            else if (settings.Scale == ScaleMode.UpscaleCanvas)
            {
                //Same as downscaleonly, except areaSize isn't changed.
                if (PolygonMath.FitsInside(manualCropSize, targetSize))
                {
                    //The image is smaller or equal to its target polygon.

                    //Use manual copy rect/size instead.

                    targetSize = manualCropSize;
                    copyRect   = manualCropRect;
                }
            }


            //May 12: require max dimension and round values to minimize rounding differences later.
            areaSize.Width    = Math.Max(1, (float)Math.Round(areaSize.Width));
            areaSize.Height   = Math.Max(1, (float)Math.Round(areaSize.Height));
            targetSize.Width  = Math.Max(1, (float)Math.Round(targetSize.Width));
            targetSize.Height = Math.Max(1, (float)Math.Round(targetSize.Height));
        }