Пример #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
        protected bool LegacyDrawWatermark(ImageState s)
        {
            Graphics g = s.destGraphics;

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

            string watermark;

            if (!LegacyParseWatermark(s.settings, out watermark))
            {
                return(false);
            }

            RectangleF imageBox = PolygonMath.GetBoundingBox(s.layout["image"]);

            //Floor and ceiling values to prevent fractional placement.
            imageBox.Width  = (float)Math.Floor(imageBox.Width);
            imageBox.Height = (float)Math.Floor(imageBox.Height);
            imageBox.X      = (float)Math.Ceiling(imageBox.X);
            imageBox.Y      = (float)Math.Ceiling(imageBox.Y);

            SizeF watermarkSize      = this.watermarkSize;
            SizeF topLeftPadding     = this.topLeftPadding;
            SizeF bottomRightPadding = this.bottomRightPadding;

            //Load the file specified in the querystring,
            Bitmap wb = GetMemCachedBitmap(watermark);

            lock (wb) {
                //If percentages, resolve to pixels
                if (valuesPercentages)
                {
                    //Force into 0..1 range, inclusive.
                    watermarkSize.Height      = Math.Max(0, Math.Min(1, watermarkSize.Height));
                    watermarkSize.Width       = Math.Max(0, Math.Min(1, watermarkSize.Width));
                    topLeftPadding.Height     = Math.Max(0, Math.Min(1, topLeftPadding.Height));
                    topLeftPadding.Width      = Math.Max(0, Math.Min(1, topLeftPadding.Width));
                    bottomRightPadding.Height = Math.Max(0, Math.Min(1, bottomRightPadding.Height));
                    bottomRightPadding.Width  = Math.Max(0, Math.Min(1, bottomRightPadding.Width));

                    //Make sure everything adds up to 1
                    double totalWidth = watermarkSize.Width + topLeftPadding.Width + bottomRightPadding.Width;
                    if (totalWidth > 1)
                    {
                        totalWidth                = 1 / totalWidth; //Turn it into the factor we have to multiple by to make everything fit.
                        watermarkSize.Width      *= (float)totalWidth;
                        topLeftPadding.Width     *= (float)totalWidth;
                        bottomRightPadding.Width *= (float)totalWidth;
                    }
                    double totalHeight = watermarkSize.Height + topLeftPadding.Height + bottomRightPadding.Height;
                    if (totalHeight > 1)
                    {
                        totalHeight                = 1 / totalHeight; //Turn it into the factor we have to multiply by to make everything fit.
                        watermarkSize.Height      *= (float)totalHeight;
                        topLeftPadding.Height     *= (float)totalHeight;
                        bottomRightPadding.Height *= (float)totalHeight;
                    }

                    //Now, we can resolve the percentages to pixels.
                    watermarkSize.Height      *= imageBox.Height;
                    watermarkSize.Width       *= imageBox.Width;
                    topLeftPadding.Height     *= imageBox.Height;
                    topLeftPadding.Width      *= imageBox.Width;
                    bottomRightPadding.Height *= imageBox.Height;
                    bottomRightPadding.Width  *= imageBox.Width;
                }

                //Keep aspect ratio, shrinking further if needed.
                if (keepAspectRatio)
                {
                    watermarkSize = PolygonMath.DownScaleInside(wb.Size, watermarkSize);
                }


                //Floor all values to avoid rounding errors and blurry lines.
                watermarkSize      = new SizeF((float)Math.Floor(watermarkSize.Width), (float)Math.Floor(watermarkSize.Height));
                topLeftPadding     = new SizeF((float)Math.Floor(topLeftPadding.Width), (float)Math.Floor(topLeftPadding.Height));
                bottomRightPadding = new SizeF((float)Math.Floor(bottomRightPadding.Width), (float)Math.Floor(bottomRightPadding.Height));


                //Check boundingbox
                SizeF watermarkBoundingBox = new SizeF(watermarkSize.Width + topLeftPadding.Width + bottomRightPadding.Width,
                                                       watermarkSize.Height + topLeftPadding.Height + bottomRightPadding.Height);

                //Don't draw the watermark if it is too small.
                if (!PolygonMath.FitsInside(watermarkBoundingBox, imageBox.Size))
                {
                    if (hideIfTooSmall)
                    {
                        return(true);
                    }
                    else
                    {
                        SizeF oldSize = watermarkBoundingBox;
                        watermarkBoundingBox  = PolygonMath.DownScaleInside(watermarkBoundingBox, imageBox.Size);
                        watermarkSize.Width  -= (oldSize.Width - watermarkBoundingBox.Width);
                        watermarkSize.Height -= (oldSize.Height - watermarkBoundingBox.Height);
                    }
                }
                //Floor all values again
                watermarkSize      = new SizeF((float)Math.Floor(watermarkSize.Width), (float)Math.Floor(watermarkSize.Height));
                topLeftPadding     = new SizeF((float)Math.Floor(topLeftPadding.Width), (float)Math.Floor(topLeftPadding.Height));
                bottomRightPadding = new SizeF((float)Math.Floor(bottomRightPadding.Width), (float)Math.Floor(bottomRightPadding.Height));



                float innerWidth  = (float)Math.Floor(imageBox.Width - Math.Abs(topLeftPadding.Width) - Math.Abs(bottomRightPadding.Width));
                float innerHeight = (float)Math.Floor(imageBox.Height - Math.Abs(topLeftPadding.Height) - Math.Abs(bottomRightPadding.Height));

                float x = 0;
                float y = 0;

                if (align == ContentAlignment.BottomCenter || align == ContentAlignment.BottomLeft || align == ContentAlignment.BottomRight)
                {
                    y = (innerHeight - watermarkSize.Height) + topLeftPadding.Height;
                }

                if (align == ContentAlignment.MiddleCenter || align == ContentAlignment.MiddleLeft || align == ContentAlignment.MiddleRight)
                {
                    y = (innerHeight - watermarkSize.Height) / 2 + topLeftPadding.Height;
                }

                if (align == ContentAlignment.TopCenter || align == ContentAlignment.TopLeft || align == ContentAlignment.TopRight)
                {
                    y = topLeftPadding.Height;
                }


                if (align == ContentAlignment.BottomRight || align == ContentAlignment.MiddleRight || align == ContentAlignment.TopRight)
                {
                    x = (innerWidth - watermarkSize.Width) + topLeftPadding.Width;
                }

                if (align == ContentAlignment.BottomCenter || align == ContentAlignment.MiddleCenter || align == ContentAlignment.TopCenter)
                {
                    x = (innerWidth - watermarkSize.Width) / 2 + topLeftPadding.Width;
                }

                if (align == ContentAlignment.BottomLeft || align == ContentAlignment.MiddleLeft || align == ContentAlignment.TopLeft)
                {
                    x = topLeftPadding.Width;
                }

                //Draw watermark
                g.DrawImage(wb, new Rectangle((int)(x + imageBox.X), (int)(y + imageBox.Y), (int)watermarkSize.Width, (int)watermarkSize.Height));
            }
            return(true);
        }
Пример #3
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));
        }
Пример #4
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);
        }