3

I'm trying to create a resizable image overlay (for cropping purposes). It seems pretty easy to resize the overlay if I ignore the aspect ratio, but I can't figure out how to perform a constrained resize that respects the AR. I figure that I obviously can't obey the overlay's "grip" positions (or even borders) unless I force the mouse to follow it, but that seems unnatural, so I'll just have to rely on the mouse gesture (which I don't mind doing).

I can also easily resize the overlay and then force it into the proper dimensions afterwards (like every other question about this topic on this site is about), but it's not very intuitive when using a mouse.

This is sort of what I'm going for: http://deepliquid.com/projects/Jcrop/demos.php?demo=live_crop

I've written an application like this before but it was browser-based so I used a javascript library. This is a desktop application and I haven't found a suitable library for this.

I've left a lot of details out of this code snippet and simplified some conditions with booleans.

private void pbImage_Paint(object sender, PaintEventArgs e)
{
    //Overlay
    e.Graphics.FillRectangle(brushRect, overlayRect);

    // Grips
    e.Graphics.FillRectangle(gripRect, leftTopGrip);
    e.Graphics.FillRectangle(gripRect, rightTopGrip);
    e.Graphics.FillRectangle(gripRect, leftBottomGrip);
    e.Graphics.FillRectangle(gripRect, rightBottomGrip);

    AdjustGrips();

    base.OnPaint(e);
}

public void AdjustGrips()
{
    // The next section only causes the grips to partly obey
    // the AR - the rest of the overlay ignores it
    if (overlayRect.Height * arWidth <= overlayRect.Width)
        overlayRect.Width = overlayRect.Height * arWidth;
    else if (overlayRect.Width * arHeight <= overlayRect.Height)
        overlayRect.Height = overlayRect.Width * arHeight;

    leftTopGrip.X = overlayRect.Left;
    leftTopGrip.Y = overlayRect.Top;

    rightTopGrip.X = overlayRect.Right - rightTopGrip.Width;
    rightTopGrip.Y = overlayRect.Top;

    leftBottomGrip.Y = overlayRect.Bottom - leftBottomGrip.Height;
    leftBottomGrip.X = overlayRect.Left;

    rightBottomGrip.X = overlayRect.Right - rightBottomGrip.Width;
    rightBottomGrip.Y = overlayRect.Bottom - rightBottomGrip.Height;

}


private void pbImage_MouseMove(object sender, MouseEventArgs e)
{
    Point pt = new Point(e.X, e.Y);

    // Details elided


    if (e.Button == MouseButtons.Left && mouseinGrip)
    {
        if (bottomRightIsGripped)
        {
            newOverlayRect.X = overlayRect.X;
            newOverlayRect.Y = overlayRect.Y;
            newOverlayRect.Width = pt.X - newOverlayRect.Left;
            newOverlayRect.Height = pt.Y - newOverlayRect.Top;

            if (newOverlayRect.X > newOverlayRect.Right)
            {
                newOverlayRect.Offset(-width, 0);
                if (newOverlayRect.X < 0)
                    newOverlayRect.X = 0;
            }

            if (newOverlayRect.Y > newOverlayRect.Bottom)
            {
                newOverlayRect.Offset(0, -height);
                if (newOverlayRect.Y < 0)
                    newOverlayRect.Y = 0;
            }

            pbImage.Invalidate();
            oldOverlayRect = overlayRect = newOverlayRect;
            Cursor = Cursors.SizeNWSE;
        }

        // Code for other grips elided
    }   

    AdjustGrips();
    pbImage.Update();
    base.OnMouseMove(e);
}

// Mouse up and down elided
4

2 回答 2

3

您可以在拖动时完全控制叠加层的新大小。

您提供的示例链接只是根据单击选择一个起点,然后选择Max(Abs(pt.x - start.x), Abs(pt.y - start.y)),并以此为基础进行裁剪。

要使用非平方比,请先标准化距离。

// given known data 
// 
// Point start; 
// The starting location of the mouse down for the drag, 
// or the top left / bottom right of the crop based on if the mouse is 
// left/above the starting point
// 
// Size ratio;
// The ratio of the result crop
//

// pt = (20)x(-20)
// start = (0),(0)
// ratio = (1)x(2)
var dist = new Point(pt.X - start.X, pt.Y - start.Y);

// "normalize" the vector from the ratio
// normalized vector is the distances with respect to the ratio
// ratio is (1)x(2). A (20)x(-20) is normalized as (20),(-10)
var normalized = new Point(dist.X / ratio.Width, dist.Y / ratio.Height);

// In our (20),(-10) example, we choose the ratio's height 20 as the larger normal.
// we will base our new size on the height
var largestNormal = (Math.Abs(normalized.X) > Math.Abs(normalized.Y)
                        ? Math.Abs(normalized.X) : Math.Abs(normalized.Y);

// The calcedX will be 20, calcedY will be 40
var calcedOffset = (largestNormal * ratio.Width, largestNormal * ratio.Height);

// reflect the calculation back to the correct quarter
// final size is (20)x(-40)
if (distX < 0) calcedOffset.X *= -1;
if (distY < 0) calcedOffset.Y *= -1;

var newPt = new Point(start.X + calcedOffset.X, start.Y + calcedOffset.Y);

请注意,其中一个长度可以大于鼠标位置,但永远不会小于鼠标位置。这将产生鼠标沿新裁剪框边缘移动的效果,并保持框的比例。

于 2013-09-13T16:40:23.950 回答
2

我已经弄清楚是什么导致了我的代码中的原始问题。与静态图像调整大小不同,纵横比代码取决于您“握住”的手柄,因此在所有情况下(例如,设置手柄位置时)将其放在一个公共位置将不起作用。您可以轻松计算下一次更新时矩形的大小,但应根据握住的手柄设置位置。

例如,如果您通过握住左上角的手柄来调整大小,则裁剪矩形的底部和右侧应保持静止。如果您保持代码不变,则矩形会正确调整大小,但它会在画布周围移动和/或夹点与矩形的角不同步。可能有更好的方法来做到这一点,但这里有一些有效的粗略代码。我只包含了右下角和左上角的代码来说明差异。省略了设置鼠标指针和错误检查等无关内容。

private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    Point mousePosition = new Point(e.X, e.Y);

    if (e.Button == MouseButtons.Left)
    {
        // This resizeMode, moveMode and other booleans
        // are set in the MouseUp event

        if (resizeBottomLeft)
        {
            // Top and Right should remain static!
            newCropRect.X = mousePosition.X;
            newCropRect.Y = currentCropRect.Y;
            newCropRect.Width = currentCropRect.Right - mousePosition.X;
            newCropRect.Height = mousePosition.Y - newCropRect.Top;

            if (newCropRect.X > newCropRect.Right)
            {
                newCropRect.Offset(cropBoxWidth, 0);
                if (newCropRect.Right > ClientRectangle.Width)
                    newCropRect.Width = ClientRectangle.Width - newCropRect.X;
            }

            if (newCropRect.Y > newCropRect.Bottom)
            {
                newCropRect.Offset(0, -cropBoxHeight);
                if (newCropRect.Y < 0)
                    newCropRect.Y = 0;
            }

            // Aspect Ratio + Positioning
            if (newCropRect.Width > newCropRect.Height)
            {
                newCropRect.Height = (int)(newCropRect.Width / ASPECT_RATIO);
            }
            else
            {
                int newWidth = (int)(newCropRect.Height * ASPECT_RATIO);
                newCropRect.X = newCropRect.Right - newWidth;
                newCropRect.Width = newWidth;
            }
        }
        else if (resizeTopRight)
        {
            // Bottom and Left should remain static!
            newCropRect.X = oldCropRect.X;
            newCropRect.Y = mousePosition.Y;
            newCropRect.Width = mousePosition.X - newCropRect.Left;
            newCropRect.Height = oldCropRect.Bottom - mousePosition.Y;

            if (newCropRect.X > newCropRect.Right)
            {
                newCropRect.Offset(-cropBoxWidth, 0);
                if (newCropRect.X < 0)
                    newCropRect.X = 0;
            }
            if (newCropRect.Y > newCropRect.Bottom)
            {
                newCropRect.Offset(0, cropBoxHeight);
                if (newCropRect.Bottom > ClientRectangle.Height)
                    newCropRect.Y = ClientRectangle.Height - newCropRect.Height;
            }

            // Aspect Ratio + Positioning
            if (newCropRect.Width > newCropRect.Height)
            {
                int newHeight = (int)(newCropRect.Width / ASPECT_RATIO);
                newCropRect.Y = newCropRect.Bottom - newHeight;
                newCropRect.Height = newHeight;
            }
            else
            {
                int newWidth = (int)(newCropRect.Height * ASPECT_RATIO);
                newCropRect.Width = newWidth;
            }
        }
        else if (moveMode) //Moving the rectangle
        {
            newMousePosition = mousePosition;
            int dx = newMousePosition.X - oldMousePosition.X;
            int dy = newMousePosition.Y - oldMousePosition.Y;
            currentCropRect.Offset(dx, dy);
            newCropRect = currentCropRect;
            oldMousePosition = newMousePosition;
        }

        if (resizeMode || moveMode)
        {
            oldCropRect = currentCropRect = newCropRect;

            // Set the new position of the grips
            AdjustGrips();
            pictureBox1.Invalidate();
            pictureBox1.Update();
        }
    }
}
于 2013-09-16T17:09:44.163 回答