Pregunta

Dado un UIImage de cualquier dimensión, deseo generar un cuadrado " icono " Versión de tamaño, px a un lado, sin distorsión (estiramiento). Sin embargo, me encuentro con un pequeño inconveniente. No estoy seguro de dónde está el problema. Esto es lo que estoy haciendo hasta ahora.

Primero, dada una UImage size , determino tres cosas: la ratio para usar cuando se reduce la imagen; un delta (la diferencia entre nuestro tamaño de icono deseado y el lado más largo) y un offset (que se utiliza para determinar nuestra coordenada de origen al recortar la imagen):

 if (size.width > size.height) {
   ratio = px / size.width;
   delta = (ratio*size.width - ratio*size.height);
   offset = CGPointMake(delta/2, 0);
 } else {
   ratio = px / size.height;
   delta = (ratio*size.height - ratio*size.width);
   offset = CGPointMake(0, delta/2);
 }

Ahora, supongamos que tiene una imagen de 640 px de ancho por 480 px de alto, y queremos obtener un icono de 50 px x 50 px de esto. El ancho es mayor que la altura, por lo que nuestros cálculos son:

ratio = 50px / 640px = 0.078125
delta = (ratio * 640px) - (ratio * 480px) = 50px - 37.5px = 12.5px
offset = {x=6.25, y=0}

A continuación, creo un CGRect rect que es lo suficientemente grande como para recortarlo al tamaño de icono deseado sin distorsión, más un clipRect con fines de recorte:

CGRect rect = CGRectMake(0.0, 0.0, (ratio * size.width) + delta,
                                   (ratio * size.height) + delta);
CGRect clipRect = CGRectMake(offset.x, offset.y, px, px);

Sustituyendo nuestros valores desde arriba, obtenemos:

rect = origin {x=0.0, y=0.0}, size {width=62.5, height=50.0}
clipRect = origin {x=6.25, y=0}, size {width=50.0, height=50.0}

Así que ahora tenemos un rectángulo de 62.5px de ancho por 50px de alto para trabajar, y un rectángulo de recorte que agarra el '' medio '' Porción de 50x50.

¡A la recta final! Luego, configuramos nuestro contexto de imagen, dibujamos el UIImage (llamado myImage aquí) en el rectángulo, establecemos el rectángulo de recorte, obtenemos la imagen (presumiblemente ahora recortada), Úsalo, y finalmente limpia nuestro contexto de imagen:

 UIGraphicsBeginImageContext(rect.size);
 [myImage drawInRect:rect]; 
 UIRectClip(clipRect);
 UIImage *icon = UIGraphicsGetImageFromCurrentImageContext();

 // Do something with the icon here ...

 UIGraphicsEndImageContext();

Solo un problema: ¡El recorte nunca ocurre! Terminé con una imagen de 63px de ancho x 50px de alto. :(

Quizás estoy haciendo un mal uso / malentendido UIRectClip ? He intentado barajar varias cosas: intercambiando el uso de rect y clipRect , moviendo UIRectClip antes de drawInRect: . No hay dados.

Intenté buscar un ejemplo de este método en línea también, sin éxito. Para el registro, UIRectClip se define como:

  

Modifica el trazado de recorte actual mediante   intersectándolo con el especificado   rectángulo.

Mezclar las cosas nos acerca un poco más:

UIGraphicsBeginImageContext(clipRect.size);
UIRectClip(rect);
[myImage drawInRect:rect];

Ahora no tenemos distorsión, pero la imagen recortada no está centrada en el original como esperaba. Aún así, al menos la imagen es de 50x50, aunque los nombres de las variables ahora están sucios como consecuencia de dicho barajado. (Respetuosamente dejaré el cambio de nombre como un ejercicio para el lector).

¿Fue útil?

Solución

Eureka! Tenía las cosas un poco confusas. Esto funciona:

CGRect clipRect = CGRectMake(-offset.x, -offset.y,
                             (ratio * size.width) + delta,
                             (ratio * size.height) + delta);
UIGraphicsBeginImageContext(CGSizeMake(px, px));
UIRectClip(clipRect);
[myImage drawInRect:clipRect];
UIImage *icon = UIGraphicsGetImageFromCurrentImageContext();

// Do something with the icon here ...

UIGraphicsEndImageContext();

No más necesidad de rect . El truco parece estar usando un desplazamiento negativo en el rectángulo de recorte, alineando así el origen de donde queremos capturar nuestra imagen de 50 x 50 (en este ejemplo).

Quizás haya una manera más fácil. Si es así, ¡péselo!

Otros consejos

Quería lograr algo similar, pero encontré que la respuesta del póster original no funcionó del todo. Se distorsionó la imagen. Esto puede deberse únicamente a que no publicó toda la solución y cambió algunas de las formas en que se inicializan las variables:

 (if (size.width > size.height) 
       ratio = px / size.width;

Estaba mal para mi solución (que quería usar el cuadrado más grande posible de la imagen de origen). Además, no es necesario usar UIClipRect: si configura el contexto del tamaño de la imagen que desea extraer, de todos modos no se realizará un dibujo real fuera de ese rectángulo. Es solo una cuestión de escalar el tamaño de la imagen y compensar una de las coordenadas de origen. He publicado mi solución a continuación:

+(UIImage *)makeIconImage:(UIImage *)image
{
    CGFloat destSize = 400.0;
    CGRect rect = CGRectMake(0, 0, destSize, destSize);

    UIGraphicsBeginImageContext(rect.size);

    if(image.size.width != image.size.height)
    {
        CGFloat ratio;
        CGRect destRect;

        if (image.size.width > image.size.height)
        {
            ratio = destSize / image.size.height;

            CGFloat destWidth = image.size.width * ratio;
            CGFloat destX = (destWidth - destSize) / 2.0;

            destRect = CGRectMake(-destX, 0, destWidth, destSize);

        }
        else
        {
            ratio = destSize / image.size.width;

            CGFloat destHeight = image.size.height * ratio;
            CGFloat destY = (destHeight - destSize) / 2.0;

            destRect = CGRectMake(0, destY, destSize, destHeight);
        }
        [image drawInRect:destRect];
    }
    else
    {
        [image drawInRect:rect];
    }
    UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return scaledImage;
}

las respuestas de wheeliebin son correctas pero olvidó un signo menos delante de destY

destRect = CGRectMake(0, -destY, destSize, destHeight);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top