如何将矩形缩放到另一个矩形(例如图片到窗口),保留纵横比并填充选项?

时间:2015-09-07 21:51:02

标签: c++ winapi bitmap geometry scaling

我把它放在这里是因为执行此操作的算法比应该更难找到。希望谷歌能缓存这个。

问题是:你有一个位图和一个窗口。您希望在窗口内部绘制位图,填充窗口,保持纵横比,因为窗口会调整大小。

您可能还希望能够以其他方式适应它,以便您可以在窗口“上方”绘制图像,并且窗口中的所有区域都将被填充。这将剪掉一些图像。我在答案中提出了一个简单的算法。

2 个答案:

答案 0 :(得分:1)

这是一个仅使用整数数学的实现。

该算法首先拉伸两个维度,保留纵横比。假设相应的其他维度占据整个空间,计算新的大小。在这些新尺寸中,超出可用区域的尺寸被设置为最大可能值,而另一个尺寸被缩小,保持纵横比。 (对于平移和扫描 bScale 设置为true)模式,不会超出可用空间的维度将设置为占用整个范围内。)

(注意:如果 sizePicture 是一个空矩形,则此函数返回一个矩形,从原点或中心向左伸展一个像素,向上伸展一个像素。)

RECT size_rect( RECT& rcScreen,
                RECT& sizePicture,
                bool bCenter/*,
                bool bScale*/ ) {

    int clientWidth = rcScreen.right - rcScreen.left;
    int clientHeight = rcScreen.bottom - rcScreen.top;
    int picWidth = sizePicture.right - sizePicture.left;
    int picHeight = sizePicture.bottom - sizePicture.top;
    // Calculate new content size
    int contentWidth = ::MulDiv( clientHeight, picWidth, picHeight );
    int contentHeight = ::MulDiv( clientWidth, picHeight, picWidth );

    // Adjust dimensions to fit inside client area
    if ( contentWidth > clientWidth ) {
    // To use the bScale parameter that allows the image to fill the entire
    // client area, use the following if-clause instead.
    //if (    ( bScale && ( contentWidth < clientWidth ) )
    //     || ( !bScale && ( contentWidth > clientWidth ) ) ) {
        contentWidth = clientWidth;
        contentHeight = ::MulDiv( contentWidth, picHeight, picWidth );
    } else {
        contentHeight = clientHeight;
        contentWidth = ::MulDiv( contentHeight, picWidth, picHeight );
    }

    RECT rect = { 0 };
    ::SetRect( &rect, 0, 0, contentWidth, contentHeight );
    if ( bCenter ) {
        // Calculate offsets to center content
        int offsetX = ( clientWidth - contentWidth ) / 2;
        int offsetY = ( clientHeight - contentHeight ) / 2;
        ::OffsetRect( &rect, offsetX, offsetY );
    }
    return rect;
}

答案 1 :(得分:0)

制作两个RECT。一个是您希望适合的窗口(传递到rcScreen),另一个是图片的尺寸:

(pseudo-code)
RECT window;
GetClientRect(hwnd,&window)
RECT bitmap_rect;
BITMAP bitmap;
bitmap_rect.left = bitmap_rect.top = 0;
bitmap_rect.right = bitmap.bmWidth;
bitmap_rect.bottom = bitmap.bmHeight;
RECT draw_rect = size_rect(window,bitmap_rect,true,true);

然后StretchBlt it:

StretchBlt(toDC, draw_rect.left, draw_rect.top, draw_rect.right, draw_rect.bottom, fromDC, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);

这是函数:(注意bCenter = false和Scale = true没有情况)。 ** bCenter是&#34;窗口中心图片的标志。&#34;标度为&#34;平移和扫描模式&#34;而不是&#34; letterbox,&#34;如果您使用图像作为要调整大小但不想拥有信箱的窗口背景,则非常有用。 **

RECT size_rect(RECT& rcScreen,
    RECT& sizePicture,
    bool bCenter,
    bool Scale)
{
    RECT rect = rcScreen;
    double dWidth = rcScreen.right - rcScreen.left;
    double dHeight = rcScreen.bottom - rcScreen.top;
    double dAspectRatio = dWidth / dHeight;

    double dPictureWidth = sizePicture.right - sizePicture.left;
    double dPictureHeight = sizePicture.bottom - sizePicture.top;
    double dPictureAspectRatio = dPictureWidth / dPictureHeight;

    double nNewHeight = dHeight;
    double nNewWidth = dWidth;
    double nHeightCenteringFactor = 0;
    double nWidthCenteringFactor = 0;

    double xstart = rcScreen.left;
    double ystart = rcScreen.top;

    if (dPictureAspectRatio > dAspectRatio)
    {
        if (bCenter && Scale) {
            nNewWidth = dPictureWidth*(1 / (dPictureHeight / dHeight));
            xstart = rcScreen.left - ((nNewWidth / 2) - (dWidth / 2));
        }
        else {
            nNewHeight = (int)(dWidth / dPictureWidth*dPictureHeight);
            if (bCenter)
                ystart = ((dHeight - nNewHeight) / 2) + rcScreen.top;
        }

    }
    else if (dPictureAspectRatio < dAspectRatio)
    {
        if (bCenter && Scale) {
            nNewHeight = dPictureHeight*(1 / (dPictureWidth / dWidth));
            ystart = rcScreen.top - ((nNewHeight / 2) - (dHeight / 2));
        }
        else{
            nNewWidth = (dHeight / dPictureHeight*dPictureWidth);

            if (bCenter)
                xstart = ((dWidth - nNewWidth) / 2) + rcScreen.left;
        }
    }
    SetRect(&rect, xstart, ystart, nNewWidth, nNewHeight);
    return rect;
}