c# - transformacion - Cambiar el tamaño de la imagen para que quepa en el cuadro delimitador
herramienta distorsión illustrator (9)
Un problema fácil, pero por alguna razón no puedo resolver esto hoy.
Necesito cambiar el tamaño de una imagen al tamaño máximo posible que cabe en un cuadro delimitador mientras se mantiene la relación de aspecto.
Básicamente, estoy buscando el código para completar esta función:
void CalcNewDimensions(ref int w, ref int h, int MaxWidth, int MaxHeight);
Donde w & h son la altura y el ancho originales (pulg) y la nueva altura y ancho (hacia fuera) y MaxWidth y MaxHeight definen el cuadro delimitador en el que debe caber la imagen.
Basado en la sugerencia de Eric, haría algo como esto:
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;
}
Podría haberme excedido un poco en los moldes, pero solo estaba tratando de mantener la precisión en los cálculos.
Código Python, pero tal vez te indique la dirección correcta:
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)
El siguiente código produce resultados más precisos:
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))
);
}
En función de las respuestas anteriores, aquí hay una función de 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
};
}
Encuentre cuál es más pequeño: MaxWidth / w
o MaxHeight / h
Luego multiplique w
y h
por ese número
Explicación:
Necesita encontrar el factor de escala que hace que la imagen se ajuste.
Para encontrar el factor de escala, s
, para el ancho, s
debe ser tal que: s * w = MaxWidth
. Por lo tanto, el factor de escala es MaxWidth / w
.
Del mismo modo para la altura.
El que requiere más escalado ( s
más pequeño) es el factor por el que debe escalar toda la imagen.
Intenté el código del Sr. Warren, pero no produjo resultados confiables.
Por ejemplo,
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}
Puedes ver, altura = 49 y altura = 50 en otro.
Aquí está el mío (versión basada en el código del Sr. Warren) sin la discrepancia y un ligero refactor:
// 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 ) );
}
Muy simple. :) El problema es encontrar un factor por el cual se necesita multiplicar el ancho y la altura. La solución es intentar usar uno y si no encaja, usar el otro. Asi que...
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;
}
Para ajustar la imagen (pctRect) a la ventana (wndRect) llame así
float factor=ScaleFactor(wndRect, pctRect); // Outer, inner
RectangleF resultRect=new RectangleF(0,0,pctRect.Width*factor,pctRect.Height*Factor)
Para realizar un relleno de aspecto en lugar de un ajuste de aspecto, utilice la relación más grande en su lugar. Es decir, cambie el código de Matt de Math.Min a Math.Max.
(Un relleno de aspecto no deja nada del cuadro delimitador vacío, pero puede poner parte de la imagen fuera de los límites, mientras que un ajuste de aspecto no deja ninguna imagen fuera de los límites, pero puede dejar un poco del cuadro delimitador vacío).
Tenía un problema similar y lo encontré muy útil: article . Como entendí correctamente, ¿necesita cambiar el tamaño de la imagen?