2009-07-09 43 views
34

Một vấn đề dễ dàng, nhưng vì một lý do nào đó tôi không thể hình dung điều này ngay hôm nay.Thay đổi kích thước hình ảnh để vừa với khung giới hạn

Tôi cần đổi kích thước hình ảnh thành kích thước tối đa có thể sẽ vừa với hộp giới hạn trong khi duy trì tỷ lệ khung hình.

Về cơ bản tôi đang tìm kiếm các mã để điền vào chức năng này:

void CalcNewDimensions(ref int w, ref int h, int MaxWidth, int MaxHeight); 

đâu w & h là chiều cao ban đầu và chiều rộng (in) và chiều cao mới và chiều rộng (trong) và MaxWidth và MaxHeight xác định khung giới hạn mà hình ảnh phải phù hợp.

+7

Xin đừng lạm dụng refs như thế. Tốt hơn hết là tạo một hình chữ nhật cấu trúc _immutable_ có trường chiều rộng và chiều cao, sau đó viết phương thức ExpandToBound lấy hai hình chữ nhật và trả về hình chữ nhật kết quả. Nó là dễ dàng hơn nhiều để lý do về chức năng khi bạn thực hiện chúng như _functions_. Đối số đi vào, kết quả đi ra; các chức năng không biến đổi trạng thái mà chúng không sở hữu. –

+0

@Eric Lippert - Đồng ý, ví dụ không phải là chức năng tôi thực sự triển khai, chỉ là phiên bản được luộc để tránh nhầm lẫn vấn đề với cấu trúc Hình chữ nhật hoặc những thứ khác không phải là một phần cốt lõi của vấn đề. –

Trả lời

74

Tìm mà là nhỏ hơn: MaxWidth/w hoặc MaxHeight/h Sau đó nhân wh bởi con số đó

Giải thích:

Bạn cần phải tìm các yếu tố rộng mà làm cho phù hợp với hình ảnh.

Để tìm hệ số chia tỷ lệ, s, cho chiều rộng, sau đó s phải sao cho: s * w = MaxWidth. Do đó, hệ số chia tỷ lệ là MaxWidth/w.

Tương tự cho chiều cao.

Một trong những yêu cầu mở rộng nhất (nhỏ hơn s) là yếu tố mà theo đó bạn phải mở rộng toàn bộ hình ảnh.

+0

Nếu bạn làm điều đó với phao bạn có thể thấy rằng kích thước phù hợp với hộp giới hạn là hơi tắt (đôi khi, theo một cách không thể đoán trước). Khi bạn đã xác định được thứ nguyên nào không phù hợp, có thể tốt nhất là chỉ đơn giản giả định một thứ nguyên khác chính xác là gì? –

+4

+1 Ba năm sau, ngồi ở đầu kết quả trên Google của tôi. Không ồn ào, không ồn ào. Cảm ơn! – chaosTechnician

+1

Giống như cách phản hồi của bạn không được viết bằng c, vì vậy tôi có thể áp dụng nó cho python –

0

Tôi muốn vấn đề tương tự và tôi thấy rằng rất hữu ích: article. Như tôi đã hiểu một cách chính xác, bạn cần phải thay đổi kích thước hình ảnh?

2

mã Python, nhưng có lẽ nó sẽ chỉ cho bạn đi đúng hướng:

def fit_within_box(box_width, box_height, width, height): 
    """ 
    Returns a tuple (new_width, new_height) which has the property 
    that it fits within box_width and box_height and has (close to) 
    the same aspect ratio as the original size 
    """ 
    new_width, new_height = width, height 
    aspect_ratio = float(width)/float(height) 

    if new_width > box_width: 
     new_width = box_width 
     new_height = int(new_width/aspect_ratio) 

    if new_height > box_height: 
     new_height = box_height 
     new_width = int(new_height * aspect_ratio) 

    return (new_width, new_height) 
+0

Mã của bạn hoạt động nếu hình ảnh phải được co lại, không phải nếu nó phải được mở rộng để vừa với vùng chứa. Tôi nghĩ bạn nên cập nhật các điều kiện như sau: 'if new_width> box_width hoặc new_height box_height hoặc new_width

26

Dựa trên đề nghị của Eric Tôi muốn làm điều gì đó như thế này:

private static Size ExpandToBound(Size image, Size boundingBox) 
{  
    double widthScale = 0, heightScale = 0; 
    if (image.Width != 0) 
     widthScale = (double)boundingBox.Width/(double)image.Width; 
    if (image.Height != 0) 
     heightScale = (double)boundingBox.Height/(double)image.Height;     

    double scale = Math.Min(widthScale, heightScale); 

    Size result = new Size((int)(image.Width * scale), 
         (int)(image.Height * scale)); 
    return result; 
} 

tôi có thể đã đi một chút overboard trên các phôi, nhưng tôi đã cố gắng duy trì độ chính xác trong các tính toán.

+0

đừng quên 'return result;' –

6

Để thực hiện điền một khía cạnh thay vì phù hợp với khía cạnh, hãy sử dụng tỷ lệ lớn hơn thay thế. Đó là, thay đổi mã của Matt từ Math.Min thành Math.Max.

(Một khía cạnh điền vào lá không có hộp giới hạn trống nhưng có thể đặt một số hình ảnh bên ngoài giới hạn, trong khi một hình phù hợp không để lại hình ảnh nào ngoài giới hạn nhưng có thể để trống một số ô giới hạn.)

6

Đã thử mã của ông Warren, nhưng nó không tạo ra kết quả đáng tin cậy.

Ví dụ,

ExpandToBound(new Size(640,480), new Size(66, 999)).Dump(); 
// {Width=66, Height=49} 

ExpandToBound(new Size(640,480), new Size(999,50)).Dump(); 
// {Width=66, Height=50} 

Bạn có thể thấy, height = 49 và height = 50 trong khác.

Đây là phiên bản dựa trên của tôiđang Warren) mà không có sự khác biệt và một cấu trúc lại nhẹ:

// Passing null for either maxWidth or maxHeight maintains aspect ratio while 
//  the other non-null parameter is guaranteed to be constrained to 
//  its maximum value. 
// 
// Example: maxHeight = 50, maxWidth = null 
// Constrain the height to a maximum value of 50, respecting the aspect 
// ratio, to any width. 
// 
// Example: maxHeight = 100, maxWidth = 90 
// Constrain the height to a maximum of 100 and width to a maximum of 90 
// whichever comes first. 
// 
private static Size ScaleSize(Size from, int? maxWidth, int? maxHeight) 
{ 
    if (!maxWidth.HasValue && !maxHeight.HasValue) throw new ArgumentException("At least one scale factor (toWidth or toHeight) must not be null."); 
    if (from.Height == 0 || from.Width == 0) throw new ArgumentException("Cannot scale size from zero."); 

    double? widthScale = null; 
    double? heightScale = null; 

    if (maxWidth.HasValue) 
    { 
     widthScale = maxWidth.Value/(double)from.Width; 
    } 
    if (maxHeight.HasValue) 
    { 
     heightScale = maxHeight.Value/(double)from.Height; 
    } 

    double scale = Math.Min((double)(widthScale ?? heightScale), 
          (double)(heightScale ?? widthScale)); 

    return new Size((int)Math.Floor(from.Width * scale), (int)Math.Ceiling(from.Height * scale)); 
} 
+0

Bạn shoud sử dụng Math.Round trong cả hai trường hợp trong dòng cuối cùng như trong mã của bạn, chúng tôi nhận được kết quả sai. – Thomas

4

mã sau đây tạo ra kết quả chính xác hơn:

public static Size CalculateResizeToFit(Size imageSize, Size boxSize) 
    { 
     // TODO: Check for arguments (for null and <=0) 
     var widthScale = boxSize.Width/(double)imageSize.Width; 
     var heightScale = boxSize.Height/(double)imageSize.Height; 
     var scale = Math.Min(widthScale, heightScale); 
     return new Size(
      (int)Math.Round((imageSize.Width * scale)), 
      (int)Math.Round((imageSize.Height * scale)) 
      ); 
    } 
0

Dựa trên các câu trả lời trước, đây là một chức năng Javascript:

/** 
* fitInBox 
* Constrains a box (width x height) to fit in a containing box (maxWidth x maxHeight), preserving the aspect ratio 
* @param width  width of the box to be resized 
* @param height  height of the box to be resized 
* @param maxWidth width of the containing box 
* @param maxHeight height of the containing box 
* @param expandable (Bool) if output size is bigger than input size, output is left unchanged (false) or expanded (true) 
* @return   {width, height} of the resized box 
*/ 
function fitInBox(width, height, maxWidth, maxHeight, expandable) { 
    "use strict"; 

    var aspect = width/height, 
     initWidth = width, 
     initHeight = height; 

    if (width > maxWidth || height < maxHeight) { 
     width = maxWidth; 
     height = Math.floor(width/aspect); 
    } 

    if (height > maxHeight || width < maxWidth) { 
     height = maxHeight; 
     width = Math.floor(height * aspect); 
    } 

    if (!!expandable === false && (width >= initWidth || height >= initHeight)) { 
     width = initWidth; 
     height = initHeight; 
    } 

    return { 
     width: width, 
     height: height 
    }; 
} 
1

Simpleber đơn giản. :) Vấn đề là tìm một yếu tố mà bạn cần nhân chiều rộng và chiều cao. Giải pháp là thử sử dụng một và nếu nó không vừa, hãy sử dụng cái kia. Vì vậy, ...

private float ScaleFactor(Rectangle outer, Rectangle inner) 
{ 
    float factor = (float)outer.Height/(float)inner.Height; 
    if ((float)inner.Width * factor > outer.Width) // Switch! 
     factor = (float)outer.Width/(float)inner.Width; 
    return factor; 
} 

Để phù hợp với hình ảnh (pctRect) đến cửa sổ (wndRect) gọi như thế này

float factor=ScaleFactor(wndRect, pctRect); // Outer, inner 
RectangleF resultRect=new RectangleF(0,0,pctRect.Width*factor,pctRect.Height*Factor) 
Các vấn đề liên quan