2015-09-07 2 views
2

Я помещаю это здесь, потому что алгоритм для этого найти труднее, чем нужно. Надеюсь, Google закроет это.Как масштабировать прямоугольник на другой прямоугольник (например, изображение в окне), сохраняя пропорции с возможностью заполнения?

Проблема в следующем: у вас есть растровое изображение и окно. Вы хотите нарисовать растровое изображение внутри окна, заполнив окно, сохраняя пропорции, по мере изменения размера окна.

Вы также можете захотеть установить его другим способом, чтобы вы могли нарисовать изображение «над» окном, и вся область в окне будет заполнена. Это закрепит часть изображения. Я представляю в ответе простой алгоритм для этого.

+1

MSDN [имеет хорошее обсуждение этой темы] (https:.//msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx), а также некоторый полезный пример кода. –

+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; 
} 
+0

«Масштаб» определяет способ, который он подходит для окна. Должен ли он сохранить соотношение сторон и показать всю картину, оставив части окна пустыми? Или он должен масштабировать его так, чтобы он заполнял окно, отсекая части изображения? Вот что он делает. Извините за плохое объяснение. –

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 это:

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 - это флаг для «центральной картинки в окне». Масштаб - это флаг «режим панорамирования и сканирования» вместо «почтовый ящик», который полезен, если вы используете изображение в качестве фона окна, которое вы хотите изменить, но не хотите иметь почтовые ящики. **

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; 
}