Пример #1
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);
        }
Пример #2
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));
        }
Пример #3
0
        public override void RenderTo(Resizing.ImageState s)
        {
            if (string.IsNullOrEmpty(Text))
            {
                return;
            }

            string finalText = Text;

            if (finalText.IndexOf('#') > -1)
            {
                Regex r = new Regex("\\#\\{([^}]+)\\}");
                finalText = r.Replace(finalText, delegate(Match m){
                    string val = s.settings[m.Groups[1].Value];
                    if (val == null)
                    {
                        return("");
                    }
                    else
                    {
                        return(val);
                    }
                });
            }

            SizeF      naturalSize   = SizeF.Empty;
            SizeF      unrotatedSize = SizeF.Empty;
            RectangleF bounds        = this.CalculateLayerCoordinates(s, delegate(double maxwidth, double maxheight) {
                using (Font f = GetFont())
                    using (StringFormat sf = GetFormat()){
                        naturalSize = s.destGraphics.MeasureString(finalText, f, new PointF(), sf);
                        SizeF size  = naturalSize;

                        unrotatedSize = Fill ? PolygonMath.ScaleInside(size, new SizeF((float)maxwidth, (float)maxheight)) : size;

                        if (Angle != 0)
                        {
                            size = PolygonMath.GetBoundingBox(PolygonMath.RotatePoly(PolygonMath.ToPoly(new RectangleF(new PointF(0, 0), size)), Angle)).Size;
                        }
                        if (Fill)
                        {
                            size = PolygonMath.ScaleInside(size, new SizeF((float)maxwidth, (float)maxheight));
                        }
                        f.FontFamily.Dispose();
                        return(PolygonMath.RoundPoints(size));
                    }
            }, true);

            using (Font f = GetFont()) {
                s.destGraphics.SmoothingMode      = SmoothingMode.HighQuality;
                s.destGraphics.TextRenderingHint  = Rendering; // Utils.parseEnum<TextRenderingHint>(s.settings["watermark.rendering"], this.Rendering); ;
                s.destGraphics.PixelOffsetMode    = PixelOffsetMode.HighQuality;
                s.destGraphics.InterpolationMode  = InterpolationMode.HighQualityBicubic;
                s.destGraphics.CompositingMode    = CompositingMode.SourceOver;
                s.destGraphics.CompositingQuality = CompositingQuality.HighQuality;

                s.destGraphics.ResetTransform();
                if (Angle != 0)
                {
                    s.destGraphics.RotateTransform((float)Angle);
                }
                s.destGraphics.ScaleTransform(unrotatedSize.Width / naturalSize.Width, unrotatedSize.Height / naturalSize.Height);
                s.destGraphics.TranslateTransform(bounds.X, bounds.Y, MatrixOrder.Append);
                using (StringFormat sf = GetFormat()) {
                    DrawString(s.destGraphics, finalText, f, new Point(0, 0), sf);
                }
                s.destGraphics.ResetTransform();

                f.FontFamily.Dispose();
            }
        }
Пример #4
0
        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))));
        }
Пример #5
0
        internal RectangleF GetCrop()
        {
            if (Zoom)
            {
                return(GetZoomCrop());
            }


            var salientBounds = SalientBoundingBox();

            var idealCropSize = PolygonMath.ScaleInside(TargetSize, ImageSize);

            var fits = PolygonMath.FitsInside(salientBounds.Size, idealCropSize);

            // If there's no need to crop out a salient region, then center on the salient bounding box and align within the image bounds.
            if (fits)
            {
                return(PolygonMath.AlignWithin(new RectangleF(PointF.Empty, idealCropSize),
                                               new RectangleF(PointF.Empty, ImageSize), PolygonMath.Midpoint(salientBounds)));
            }

            if (NeverCropSalientArea)
            {
                var compromiseCrop = new SizeF(Math.Max(salientBounds.Width, idealCropSize.Width),
                                               Math.Max(salientBounds.Height, idealCropSize.Height));

                // Don't worry about 3 pixels or less, that's just annoying.
                if (compromiseCrop.Width - idealCropSize.Width <= 3 &&
                    compromiseCrop.Height - idealCropSize.Height <= 3)
                {
                    compromiseCrop = idealCropSize;
                }

                return(PolygonMath.AlignWithin(new RectangleF(PointF.Empty, compromiseCrop),
                                               new RectangleF(PointF.Empty, ImageSize), PolygonMath.Midpoint(salientBounds)));
            }

            // Find the least bad crop (brute force)
            var vertical = salientBounds.Height > idealCropSize.Height;

            var choiceCount = vertical
                ? salientBounds.Height - idealCropSize.Height
                : salientBounds.Width - idealCropSize.Width;

            var searchArea = new RectangleF(vertical ? 0 : salientBounds.X, vertical ? salientBounds.Y : 0, vertical ? idealCropSize.Width : salientBounds.Width, vertical ? salientBounds.Height : idealCropSize.Height);

            var initialWindow = PolygonMath.AlignWith(new RectangleF(PointF.Empty, idealCropSize), searchArea,
                                                      ContentAlignment.TopLeft);

            double bestVolume = 0;
            var    bestCrop   = initialWindow;

            for (var i = 0; i < choiceCount; i++)
            {
                var window = initialWindow;
                window.Offset(vertical ? 0 : i, vertical ? i : 0);

                var volume = SalientArea.IntersectVolume(window, Regions);
                if (volume > bestVolume)
                {
                    bestVolume = volume;
                    bestCrop   = window;
                }
            }
            return(bestCrop);
        }